初始化列表必须写在构造函数定义的冒号后,用于直接初始化const成员、引用、无默认构造函数的类类型等;否则编译报错或引发未定义行为。

初始化列表必须写在构造函数定义的冒号后面
不写初始化列表,成员变量就只能靠赋值操作,而赋值发生在构造函数体执行时——此时对象内存已分配,但成员可能已被默认构造(比如 std::string、std::vector),再赋值就是一次无谓的构造+析构+构造。初始化列表绕过默认构造,直接调用目标类型的带参构造,省掉中间步骤。
常见错误现象:const 成员或引用类型成员在构造函数体内无法赋值,编译报错 error: uninitialized const member 或 error: uninitialized reference member;这类成员**必须且只能**在初始化列表里初始化。
- 语法位置固定:紧接在参数列表后、函数体前,用冒号开头,多个成员用逗号分隔
- 顺序不看列表书写顺序,而看类中成员声明的顺序——写反了可能导致用未初始化的成员去初始化后面的成员
- 基类构造必须放在派生类初始化列表最前面(如果有)
class A {
const int x;
std::string s;
public:
A(int v) : x(v), s("hello") {} // ✅ 正确:const 和 string 都走初始化
};
哪些情况不能用赋值替代初始化列表
除了 const 和引用类型,还有三类成员必须走初始化列表:
- 没有默认构造函数的自定义类类型成员(比如只提供了
A(int),没提供A()) - 基类子对象(尤其当基类没有默认构造函数时)
- 某些标准库类型在特定场景下性能敏感,如
std::vector<int></int>初始化为固定大小:用v(1000)比先默认构造再v.resize(1000)少一次内存重分配
典型误用:std::mutex 成员——它不可复制、不可赋值、也没有默认构造函数,声明即需初始化,否则编译失败:error: use of deleted function 'std::mutex::mutex()'。
立即学习“C++免费学习笔记(深入)”;
初始化列表里调用成员函数的风险
初始化列表中调用的成员函数,其行为是未定义的——因为此时对象尚未完成构造,虚函数表可能未就绪,非静态成员变量也未必初始化完毕。
- 即使函数不访问任何成员(比如空的
void init() {}),C++ 标准也不保证安全 - 若函数内用了
this或访问了其他成员,极大概率 crash 或读到垃圾值 - 正确做法:把逻辑移到构造函数体里,或拆成独立的
setup()函数由用户显式调用
容易被忽略的一点:初始化列表里的表达式求值顺序,严格按成员在类中声明的顺序,和你在列表里写的先后无关。写成 : b(a), a(42) 不会报错,但 b 会用未初始化的 a 初始化。
初始化列表与 delegating constructor 的配合
C++11 起支持委托构造(delegating constructor),一个构造函数可以调用另一个构造函数完成初始化。这时候初始化列表只能出现在被委托的目标构造函数里,委托方的初始化列表必须为空。
- 委托调用必须是初始化列表中唯一一项,且必须是第一个(不能和其他成员混写)
- 委托后不能再初始化任何成员,否则编译报错:
error: constructor delegates to itself或error: member initializer list is not allowed - 委托构造仍受成员声明顺序约束——被委托的构造函数内部的初始化列表才真正决定初始化顺序
class B {
int x, y;
public:
B(int a) : x(a), y(a*2) {}
B() : B(42) {} // ✅ 委托,无其他初始化项
};
复杂点在于:如果委托链太深,或者初始化逻辑分散在多个构造函数里,调试时很难一眼看出某个成员到底被谁、以什么值初始化。建议优先用单个构造函数 + 默认参数,而不是过度依赖委托。









