编译器会隐式删除默认构造函数,当类含有未初始化的引用或const成员、基类/成员不可默认构造时;必须通过初始化列表显式初始化,且无法用= default绕过语义限制。

引用成员或 const 成员没初始化器,F::F() 就被删了
编译器看到 int& ref 或 const int c 这类成员,第一反应不是“我来帮你填个默认值”,而是“你得自己说清楚它绑谁、初值是多少”。因为引用必须绑定有效对象,const 必须在构造时定值——默认构造函数啥都不传、啥都不做,根本没法满足这两条铁律。所以编译器直接把隐式声明的 F::F() 标记为 = delete,不给定义机会。
常见错误现象:F f; 编译报错 error C2280: attempting to reference a deleted function,但类定义里明明没写 = delete —— 那是编译器悄悄加的。
- 只要没提供默认成员初始化器(C++11 起支持),哪怕只有一处引用或 const 成员,就足以触发删除
- 初始化器写法必须是类内直接写,比如
int& ref = *new int(0);不合法(不能用 new 表达式),得是int& ref = some_global;或靠构造函数参数传递 - 注意:
std::string、std::vector这类有默认构造函数的类型,不会导致删除;问题只出在“无法默认构造”的成员上
基类或成员类型本身不可默认构造,Derived::Derived() 也保不住
就算你的类里全是普通 int 和 double,只要它继承自一个没默认构造函数的基类,或者包含一个没默认构造函数的成员(比如 class NoDefault { NoDefault(int); };),那你的派生类/外层类的默认构造函数照样被删。
原因很简单:编译器生成的默认构造函数,内部要调用基类和成员的默认构造函数。如果其中任何一个不存在,整个链条就断了,编译器只能放弃生成。
- 典型场景:继承
QApplication(Qt 类,构造需传argc/argv)后直接写MyApp app;→ 报错 - 检查顺序:先看基类有没有默认构造,再看每个非静态成员类型是否可默认构造
- 解决办法不是“绕开”,而是显式写构造函数,并在初始化列表里把基类和成员都喂够参数
= default 和 = delete 不是万能解药
= default 只能让编译器生成默认构造函数,但它不能绕过语义限制。比如类里仍有未初始化的引用成员,F() = default; 一样编译不过——编译器会当场拒绝定义这个函数。
而 = delete 是主动宣判,但它是显式的,和隐式删除性质不同:前者是你不让用,后者是编译器发现“没法用”才封掉。
-
= default放在类内声明处,仅当所有成员/基类都支持默认构造时才真正生效 - 写了任何其他构造函数(哪怕只是
MyClass(int)),编译器就不再隐式生成默认构造;这时想保留就得手动加MyClass() = default;,但前提是它真能成立 - 隐式删除发生在“使用点”,不是类定义时——可能头文件编译通过,直到某处
std::vector<f> v(10);</f>才爆错
最容易被忽略的点:初始化列表才是唯一真相
很多人以为“我在构造函数体里赋值了就行”,比如:
F::F(int& r, int val) {
ref = r; // 错!ref 在进入函数体前就必须绑定
c = val; // 错!c 在进入函数体前就必须初始化
}
这代码根本过不了编译。引用和 const 的初始化,**只能且必须**在初始化列表里完成:
F::F(int& r, int val) : ref(r), c(val) {}
这也是为什么隐式删除无法被“事后补救”——默认构造函数连初始化列表都没有,自然没地方写 ref(r), c(val)。
复杂点在于:一旦类结构变深(多层继承 + 多个含引用/const 的成员),哪个初始化器该写在哪一层、哪个参数从哪来,很容易漏掉一环。这时候报错信息里的“deleted function”提示,往往离真正缺初始化的地方隔了两三个间接层。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











