编译器仅在类无用户定义构造函数且所有成员和基类均可默认构造时隐式生成默认构造函数;否则需显式定义或=default。

默认构造函数和拷贝构造函数不是“写出来就自动生效”的,它们的调用时机、隐式生成规则、是否被编译器删除,全取决于你有没有显式定义其他构造函数、成员是否可复制、类是否有 const 或引用成员。
什么时候编译器会自动生成默认构造函数?
只有当类中没有用户定义的任何构造函数,且所有非静态数据成员和基类都可默认构造时,编译器才隐式声明并定义一个默认构造函数(函数体为空)。一旦你写了任意一个构造函数(哪怕带默认参数),编译器就不再生成默认构造函数。
- 错误现象:
MyClass obj;编译失败,报错no matching function for call to 'MyClass::MyClass()'—— 很可能因为你写了MyClass(int x)却忘了加默认构造函数 - 若类中有
const int val;或int& ref;成员,即使没写任何构造函数,编译器也无法生成默认构造函数(因为这些成员必须在初始化列表中初始化) - 基类或成员类型本身没有默认构造函数(如某个成员是
std::mutex),也会导致编译器拒绝生成默认构造函数
拷贝构造函数的调用场景与隐式删除条件
拷贝构造函数在对象以值方式传参、返回局部对象、用已有对象初始化新对象(MyClass b = a;)时触发。C++17 后部分场景会强制省略拷贝(RVO/NRVO),但语义上仍要求该函数存在且可访问。
- 如果你定义了移动构造函数但没定义拷贝构造函数,且没显式
= default,编译器不会自动生成拷贝构造函数(它被隐式删除) - 类中有
const成员或引用成员时,编译器生成的默认拷贝构造函数仍有效(只要这些成员在初始化列表中被正确绑定);但若你手动写了构造函数却漏掉某个引用成员的初始化,就会编译失败 - 拷贝构造函数形参必须是
const MyClass&(或至少是引用类型),否则会引发无限递归:用MyClass(MyClass)定义会导致传参时再次调用自身
如何安全地禁用或委托构造函数?
用 = delete 显式禁用,比不写更明确;用 = default 强制让编译器生成,比手写更安全(尤其含虚函数、继承关系复杂时)。
立即学习“C++免费学习笔记(深入)”;
- 禁用拷贝:
MyClass(const MyClass&) = delete;和MyClass& operator=(const MyClass&) = delete;要成对出现 - 显式委托默认构造:
MyClass() = default;—— 这样即使你写了其他构造函数,MyClass obj;仍合法 - 若类含
std::unique_ptr等不可拷贝成员,又想支持拷贝,必须手写拷贝构造函数并做深拷贝,不能依赖= default
初始化列表比赋值更关键
构造函数体内写的 member = value; 是赋值,不是初始化;而初始化列表 : member(value) 才是真正的构造阶段调用。对于 const、引用、无默认构造函数的成员,初始化列表是唯一合法写法。
- 错误写法:
class A { const int x; A() { x = 10; } // 编译错误:const 成员必须在初始化列表中初始化 }; - 性能影响:对自定义类型成员,初始化列表直接调用构造函数;赋值则先调用默认构造,再调用赋值运算符 —— 多一次构造开销
- 基类构造必须在初始化列表中调用,不能放到函数体里
真正容易出问题的地方,往往不在“怎么写语法”,而在“编译器悄悄不生成”和“看似能用实则已删除”。检查构造行为最直接的办法,是把拷贝/默认构造函数设为 private 或 = delete,看哪些地方立刻报错 —— 那就是它们实际被调用的位置。










