核心要点速览

  • 访问控制:public(接口)、private(实现)、protected(继承)
  • const 成员:const 成员变量需初始化列表初始化;const 成员函数不可修改成员变量
  • static 成员:静态成员变量类共享、类外初始化;静态成员函数无 this 指针、仅访问静态成员
  • 构造函数:可重载、非虚函数;含默认 / 带参 / 拷贝 / 移动构造
  • 析构函数:不可重载;基类需设为虚函数避免资源泄漏
  • 类大小:仅取决于非静态数据成员,遵循内存对齐

一、基本概念

  • 类:抽象的数据类型模板,定义对象的属性(数据成员)和行为(成员函数)。
  • 对象:类的具体实例,占用实际内存,拥有类定义的属性和行为。

二、核心特性:封装

核心是 “隐藏实现细节,暴露公共接口”,通过访问控制符实现。

访问控制符 可访问范围 作用
public 类内、类外、派生类 暴露接口,供外部调用
private 仅类内和友元 隐藏数据成员和内部实现
protected 类内、友元、派生类(类外不可访问) 为继承预留访问权限
  • 封装意义:保障数据安全、提升代码维护性、降低使用复杂度。

三、构造函数:对象的初始化

  • 核心特点:函数名与类名相同,无返回值,对象创建时自动调用,可重载,不能为虚函数。

常见类型

1. 默认构造函数

  • 定义:无参数,或所有参数都有默认值。
  • 编译器行为:未定义任何构造函数时自动生成;定义其他构造函数后需手动定义。

2. 带参构造函数

  • 用途:通过参数灵活初始化对象。

3. 拷贝构造函数

  • 定义:参数必须为 “同类对象的 const 引用”(避免无限递归)。
  • 调用时机:对象初始化、函数按值传参、函数返回值为对象(非引用)。
  • 浅拷贝 vs 深拷贝:
    • 浅拷贝:仅复制表层数据,指针成员共享内存,可能导致双重释放。
    • 深拷贝:为指针成员重新分配内存并复制数据,示例:
1
2
3
4
5
6
7
8
9
10
class String {
private:
char* str;
public:
String(const String& other) {
str = new char[strlen(other.str) + 1];
strcpy(str, other.str);
}
~String() { delete[] str; }
};

4. 移动构造函数

  • 核心:接管原对象动态资源,避免拷贝,提升效率。
  • 示例:
1
2
3
4
5
6
7
8
9
10
class String {
private:
char* str;
public:
String(String&& other) noexcept {
str = other.str;
other.str = nullptr; // 掏空原对象
}
};
String s2 = std::move(s1); // 接管s1资源

初始化列表

  • 用途:优先初始化成员变量,推荐使用。
  • 必须使用的场景:const 成员、引用成员、无默认构造函数的类成员,示例:
1
2
3
4
5
6
7
class A {
private:
const int a;
int& b;
public:
A(int x, int& y) : a(x), b(y) {} // 初始化列表
};

四、析构函数:对象的清理

  • 特点:函数名~类名,无参数、无返回值,不可重载,对象生命周期结束时自动调用。
  • 必须手动定义的场景:类包含动态分配资源(如 new 的内存)。
  • 虚析构函数(基类必备,避免资源泄漏):
1
2
3
4
5
6
7
8
class Base {
public:
virtual ~Base() { delete[] data; } // 虚析构
};
class Derived : public Base {
public:
~Derived() { delete[] derivedData; }
};

五、this 指针

  • 本质:非静态成员函数中隐式传递的指针,指向当前调用对象(类型类名* const)。
  • 用途:
1
2
void setName(string name) { this->name = name; } // 区分成员与参数
Person& setAge(int age) { return *this; } // 链式调用

六、静态成员(属于类)

1. 静态成员变量

  • 特点:所有对象共享,存储在全局数据区,类内声明、类外初始化:
1
2
3
4
5
class Counter {
public:
static int count; // 类内声明
};
int Counter::count = 0; // 类外初始化

2. 静态成员函数

  • 特点:无 this 指针,无需创建对象即可调用,仅能访问静态成员:
1
2
static void increment() { count++; } // 仅访问静态成员
Counter::increment(); // 直接调用

七、类的大小计算

  • 规则:仅取决于非静态数据成员,遵循内存对齐,与成员函数、静态成员无关。
  • 示例:
1
2
3
4
class Empty {};
sizeof(Empty); // 1字节(占位)
class A { char c; int i; };
sizeof(A); // 8字节(1+3填充+4,对齐系数4)

八、问答

  • 构造函数和析构函数的调用顺序?
    • 构造:先定义先构造;析构:后构造先析构。
  • 拷贝构造函数的参数为什么必须是引用?
    • 避免值传递引发的无限递归(形参初始化需再次调用拷贝构造)。