构造函数必须用初始化列表初始化const成员,因其只能在对象创建时赋值;同理引用及无默认构造函数的类类型成员也需如此;初始化顺序依声明顺序而非列表顺序;初始化列表避免自定义类型额外构造析构开销;禁止在其中调用非静态成员函数。

构造函数里为什么必须用初始化列表初始化 const 成员
因为 const 成员一旦声明,就只能在对象诞生那一刻赋值,不能等到构造函数体里再“赋值”——那已经算修改了。编译器会直接报错:error: uninitialized const member 或类似提示。
同理,引用类型(int&)、没有默认构造函数的类类型成员,也必须走初始化列表。
- 错误写法:
class A { const int x; A() { x = 10; } };→ 编译失败 - 正确写法:
class A { const int x; A() : x(10) {} }; - 如果漏掉某个
const成员,GCC/Clang 通常报uninitialized const member,MSVC 可能更模糊,比如member 'A::x' must be initialized
初始化列表顺序和定义顺序不一致会出什么问题
初始化列表里写的顺序无关紧要,真正起作用的是成员在类中声明的顺序。如果依赖未初始化的成员去初始化另一个成员,就会触发未定义行为——哪怕代码看起来“逻辑通顺”。
比如:
class B { int a; int b; B() : b(a + 1), a(42) {} }; 实际上先初始化 a(声明在前),再初始化 b,但 b 的初始化表达式里用了 a,此时 a 还没被赋值(只是分配了内存),结果是读取垃圾值。
立即学习“C++免费学习笔记(深入)”;
- 编译器一般不会警告,运行时行为不可预测
- Clang 加
-Wreorder、GCC 加-Wreorder可捕获这类问题 - 安全做法:让初始化列表顺序和声明顺序严格一致,减少脑力负担
初始化列表比构造函数体内赋值快在哪
对自定义类型(比如 std::string、std::vector),初始化列表调用的是构造函数;而函数体内写 member = value; 走的是赋值运算符——这意味着先默认构造,再赋值,多一次构造+一次析构开销。
例如:
class C { std::string s; C() : s("hello") {} }; → 一次构造class C { std::string s; C() { s = "hello"; } }; → 默认构造 + operator=(内部可能涉及内存重分配)
- 内置类型(
int、double)没差别,但统一用初始化列表更一致 - 移动语义下,函数体内赋值可能触发移动赋值,仍不如直接构造干净
- 性能差异在高频创建对象(如容器 resize、循环 new)时才明显,但习惯值得养成
初始化列表里调用成员函数或 this 指针安全吗
不安全。初始化列表执行时,对象尚未完全构造,基类和成员都还没初始化完毕,此时调用虚函数会调用当前类的版本(不是最终派生类的),调用普通成员函数则可能访问未初始化的成员变量。
例如:
class D { int x; D() : x(init_x()) {} int init_x() { return x * 2; } }; → x 此时是未定义值,init_x() 返回垃圾结果。
- 禁止在初始化列表中调用任何成员函数(包括
this->xxx()) - 可以调用静态成员函数、全局函数、constexpr 函数(只要不依赖未初始化状态)
- 尤其警惕 IDE 自动补全出来的
this->,删掉它
最常被忽略的一点:初始化列表不是“按行执行”的普通代码块,它是对象生命期的第一阶段,语义上比函数体更“底层”。写的时候得时刻想着“此刻哪些东西还不存在”。









