构造函数初始化列表必须写在参数括号后、函数体前,用冒号开头,是唯一能初始化const成员、引用成员及无默认构造函数类成员的方式;赋值则发生在构造函数体内,需先默认构造再赋值,效率低且对三类成员不合法。

构造函数初始化列表怎么写,和赋值有啥区别
它不是语法糖,是唯一能初始化 const 成员、引用成员、没有默认构造函数的类成员的方式。比如 const int x 或 MyClass& ref,在构造函数体里用 x = 5 直接报错——编译器不让你“赋值”,只允许“初始化”。
实操上,初始化列表写在构造函数参数括号后、函数体前,用冒号开头:
MyClass(int a) : x(a), ref(other_obj), obj(a, "hello") { }
而构造函数体内写 x = a 是赋值操作,前提是 x 已经被默认构造过(对内置类型是未定义行为,对类类型会先调一次默认构造,再调一次赋值运算符)。
为什么初始化列表更快?关键在避免多余构造
对自定义类型成员,初始化列表直接调用目标构造函数;赋值方式则先调默认构造,再调赋值运算符——多一次函数调用、多一次内存初始化,甚至可能触发临时对象拷贝。
立即学习“C++免费学习笔记(深入)”;
-
std::vector<int> data;</int>在初始化列表中写data(1000)→ 一次性分配 1000 元素空间 - 若在函数体里写
data = std::vector<int>(1000)</int>→ 先默认构造空vector,再移动/拷贝赋值,至少多一次内存分配判断 - 对于无默认构造函数的类(如只有
explicit MyClass(int)),不用初始化列表就根本编译不过
哪些成员必须用初始化列表
三类成员绕不开初始化列表,否则编译失败:
-
const成员:如const std::string name; - 引用成员:如
int& value;(引用必须绑定,不能“赋值”) - 没有默认构造函数的类类型成员:如
std::unique_ptr<t> ptr;</t>(它的默认构造是合法的,但如果你自定义了一个NonDefault(int)类,就没默认构造)
顺带一提:std::unique_ptr、std::mutex 这类不可复制的对象,初始化列表也是最安全的起点——避免在函数体内误触发拷贝或隐式转换。
容易踩的初始化顺序坑
初始化顺序**只取决于成员在类中声明的顺序**,和初始化列表里写的先后无关。这点极易被忽略,导致未定义行为。
比如:
class Bad {
int a;
int b;
public:
Bad() : b(1), a(b + 1) {} // ❌ a 仍用未初始化的 b 初始化!
};
因为 a 声明在 b 前,所以先初始化 a(此时 b 还没动),再初始化 b。结果 a 是垃圾值。
解决办法只有两个:
- 调整成员声明顺序,让依赖关系从上到下自然成立
- 或者,把逻辑移出初始化列表,改用函数体内赋值(仅限非 const / 非引用 / 可默认构造的成员)
这种顺序问题不会报错,但运行结果飘忽,调试起来特别费时间。









