C++ 类编译器默认生成的十大特殊成员函数
一、完整列表(C++98 ~ C++11 统一标准)
如果类没有手动声明对应函数,编译器会隐式自动生成:
- 默认构造函数(无参构造)
- 拷贝构造函数(复制构造)
- 拷贝赋值运算符
operator= - 析构函数
- 移动构造函数(C++11 新增)
- 移动赋值运算符
operator=(C++11 新增)
前4个是C++98就有,后2个移动语义是C++11引入。
二、逐个详解生成规则、触发条件、禁用场景
1. 默认构造函数 T()
生成条件
类没有任何自定义构造函数(有参、无参都算自定义),编译器生成空无参构造。
struct A {
int x;
};
// 编译器生成:A::A() {}
A a; // 合法调用默认构造
不生成的情况
只要写了任意构造函数(哪怕带参数),编译器不再生成默认构造:
struct A {
A(int) {} // 自定义构造,无默认构造
};
A a; // 编译报错
特殊限制
- 类含const成员/引用成员:自动默认构造函数会被删除(无法初始化)。
- 成员无默认构造:本类默认构造被删除。
2. 拷贝构造函数 T(const T&)
生成条件
没有手动声明拷贝构造,编译器生成浅拷贝版本:逐成员拷贝(值拷贝)。
struct A { int x; };
// 自动生成 A(const A& other) : x(other.x) {}
A a1;
A a2 = a1; // 调用拷贝构造
不生成/被删除场景
- 手动写了拷贝构造;
- 类包含不可拷贝成员(引用、const成员、unique_ptr、无拷贝构造的成员);
- 基类拷贝构造被删除/私有。
3. 拷贝赋值 T& operator=(const T&)
生成规则
无手动重载赋值,生成逐成员赋值浅拷贝。
A a1, a2;
a2 = a1; // 调用自动拷贝赋值
自动删除场景
- 有 const / 引用成员(引用不能重新赋值);
- 成员不可赋值(unique_ptr等);
- 基类赋值运算符被删除。
4. 析构函数 ~T()
生成条件
没有手动写析构,编译器生成空析构:
struct A { int x; };
// 自动生成 ~A(){},对象销毁时调用
特殊规则
- 哪怕写了其他构造,析构依然会默认生成;
- 析构不会被自动删除,只有手动
~A() = delete;才禁用; - 基类析构建议加
virtual,否则多态释放内存泄漏(编译器不会自动virtual)。
5. 移动构造 T(T&&) C++11
生成前提(全部满足才生成)
- 没有自定义:拷贝构造、拷贝赋值、移动构造、移动赋值、析构;
- 所有非静态成员、基类都支持移动构造。
作用
转移资源所有权(std::move),性能优于拷贝。
触发删除
只要手动写了拷贝构造/析构等任一函数,编译器不再生成移动构造。
6. 移动赋值 T& operator=(T&&) C++11
生成规则和移动构造完全一致:只要手动定义拷贝/析构等,就不会自动生成。
三、记忆速记:三/五法则
1. C++98 三法则(Rule of Three)
如果你手动定义了下面任意一个,建议手动写出另外两个:
- 析构函数
- 拷贝构造
- 拷贝赋值运算符
原因:通常是管理堆内存/资源,默认浅拷贝会双重释放、内存泄漏。
2. C++11 五法则(Rule of Five)
扩展两条移动函数:
手动定义析构/拷贝构造/拷贝赋值任一,编译器不会自动生成移动构造、移动赋值;
此时资源类建议显式定义/禁用全部5个特殊函数:
- 默认构造
- 拷贝构造
- 拷贝赋值
- 移动构造
- 移动赋值
- 析构
四、示例:全部显式声明/禁用
class Demo {
public:
// 1. 默认构造
Demo() = default;
// 2. 拷贝构造
Demo(const Demo&) = delete;
// 3. 拷贝赋值
Demo& operator=(const Demo&) = delete;
// 4. 移动构造
Demo(Demo&&) = default;
// 5. 移动赋值
Demo& operator=(Demo&&) = default;
// 6. 析构
~Demo() = default;
};
五、补充:不自动生成的常用运算符(容易混淆)
下面这些不会由编译器默认生成,必须手动重载:
operator+、operator-算术运算符operator==、operator<比较运算符operator[]、operator()、operator*- 流运算符
<< / >>
只有上面6个特殊成员函数是编译器按需隐式生成。
另外四个
一、先说结论
编译器会隐式生成两个取地址重载,但它们不属于「六大特殊成员函数」,平时讲默认生成的特殊函数一般不提:
- 普通取地址:
T* operator&() - const取地址:
const T* operator&() const
1. 自动生成规则
只要你没有手动重载 operator&,编译器就会自动生成这两个版本:
struct A {
int x;
};
// 编译器隐式生成等价代码:
A* operator&() { return this; }
const A* operator&() const { return this; }
作用:直接返回当前对象 this 指针。
使用示例
A a;
A* p = &a; // 调用 operator&()
const A ca{};
const A* cp = &ca; // 调用 const 版本
2. 为什么平时讲默认函数很少提它?
(1)标准定义区分两类成员
C++标准里把成员函数分成两类:
-
特殊成员函数(special member functions):
默认构造、拷贝构造、拷贝赋值、析构、移动构造、移动赋值(共6个)
这一组有严格的生成/抑制规则(三/五法则只针对它们)。 -
内置运算符成员(如 operator&):
operator&、operator->、operator[]等不属于特殊成员;
只要不手动写,就自动生成,不受三/五法则影响。
(2)几乎没人重载 operator&
正常业务代码完全不需要改写取地址;
只有少数黑魔法库(智能指针代理、侵入式容器、调试追踪内存)才会手动重载 operator&,属于非常规操作。
3. 手动重载后,编译器不再生成默认版本
一旦自己写任意一个 operator&,编译器就不会再生成默认的两个版本:
struct A {
// 手动重载取地址,编译器不再生成默认的 &
int* operator&() {
return &x;
}
int x;
};
A a;
&A; // 调用自定义版本,返回 int*,不再返回 A*
const A ca{};
&ca; // 编译报错:没有 const 版 operator&
4. 补充:还有两个配套默认生成的一元运算符
除了 operator&,编译器还会隐式生成:
operator->(箭头):只有类是智能指针/包装器风格才会用到,普通类不会触发使用operator*(解引用):同样极少重载
总结对比
- 六大特殊成员函数(考试/面试必考):默认构造、拷贝构造、拷贝赋值、析构、移动构造、移动赋值;受三/五法则约束。
- 默认生成但不属于特殊成员:
operator&()/operator&() const;不受三/五法则约束,教材一般不列入“默认生成函数”清单。
面试如果问:“C++ 类编译器默认生成哪些成员函数?”
优先答6个特殊成员;如果面试官追问有没有其他默认生成的运算符,再补充取地址重载。

1804

被折叠的 条评论
为什么被折叠?



