核心要点速览
- 访问控制: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);
|
初始化列表
- 用途:优先初始化成员变量,推荐使用。
- 必须使用的场景: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); class A { char c; int i; }; sizeof(A);
|
八、问答
- 构造函数和析构函数的调用顺序?
- 拷贝构造函数的参数为什么必须是引用?
- 避免值传递引发的无限递归(形参初始化需再次调用拷贝构造)。