特殊成员函数的定义与分类
特殊成员函数指 C++ 编译器会自动生成的成员函数,不同标准版本涵盖范围不同:
- C++98:包含 4 种:默认构造函数、析构函数、复制构造函数、复制赋值运算符
- C++11 及以上:新增 2 种:移动构造函数、移动赋值运算符
编译器自动生成的特殊成员函数,访问层级为public、inline、非虚(除非基类析构函数为虚,派生类析构函数才会为虚)
大三律(Rule of Three)
若声明了复制构造函数、复制赋值运算符、析构函数中的任意一个,就需要同时声明这三个。
需要改写复制操作,通常意味着类涉及裸资源管理(堆内存、文件句柄等):
- 一种复制操作中做的资源管理,大概率也需要在另一种复制操作中执行;
- 析构函数需参与资源管理(通常是释放资源)。
推论:若用户声明了析构函数,说明类需手动管理裸资源,而编译器默认的'逐成员复制'仅拷贝资源标识(如指针地址),而非资源本身,会导致内存泄漏、重复释放等资源管理混乱问题。
C++11 的补充规则
- 析构函数影响:只要用户声明了析构函数,编译器不会生成移动操作(移动构造 / 移动赋值);
- 复制操作的废弃行为:在已有用户声明的复制操作或析构函数的情况下,编译器仍自动生成复制操作的行为已被废弃;
- 移动操作的生成条件(三者需同时满足):
- 类未声明任何复制操作(复制构造 / 复制赋值);
- 类未声明任何移动操作(移动构造 / 移动赋值);
- 类未声明任何析构函数。
C++11 中各类特殊成员函数的生成规则
| 函数类型 | 生成规则 | 运行期行为 |
|---|---|---|
| 默认构造函数 | 仅当类无用户声明的构造函数时生成(与 C++98 一致) | - |
| 析构函数 | 与 C++98 基本一致,仅默认带noexcept;基类析构为虚则派生类析构为虚 | - |
| 复制构造函数 | 1. 无用户声明的复制构造函数时生成;2. 声明了移动操作则被删除;3. 有析构 / 复制赋值时,自动生成行为已废弃 | 按成员复制非静态数据成员(与 C++98 一致) |
| 复制赋值运算符 | 1. 无用户声明的复制赋值运算符时生成;2. 声明了移动操作则被删除;3. 有析构 / 复制构造时,自动生成行为已废弃 | 按成员赋值非静态数据成员(与 C++98 一致) |
| 移动构造 / 移动赋值运算符 | 仅当类无用户声明的复制操作、移动操作、析构函数时生成 | 按成员移动非静态数据成员 |
成员函数模板不会抑制特殊成员函数的生成(无论是否定义,编译器仍会按规则生成特殊成员函数)。
总结
- 特殊成员函数包含默认构造、析构、复制操作(构造 / 赋值)、移动操作(构造 / 赋值),C++11 比 C++98 多移动操作;
- 移动操作仅在类无用户声明的复制操作、移动操作、析构函数时才会被自动生成;
- 复制构造 / 赋值仅在无对应用户声明时生成,声明移动操作则会删除复制操作;有显式析构函数时,自动生成复制操作的行为已废弃;
- 成员函数模板不影响特殊成员函数的自动生成。

