成员初始化列表构造函数,和普通构造函数有什么区别?
80 2025-09-22 11:03
成员初始化列表构造函数 和 普通构造函数(指在函数体内赋值)都能把对象“造”出来,但二者在执行时机、效率、功能三个维度上有本质区别。可以把它们类比成“出生时就带好装备”和“出生后再穿装备”。
1. 执行时机:谁先谁后
表格
复制
阶段 | 成员初始化列表 | 普通构造函数体 |
---|---|---|
1 | 成员变量被构造(调用其构造函数) | 同左,** unavoidable ** |
2 | 列表里给定的实参直接传给成员的构造函数 | 进入函数体,此时成员已构造完成 |
3 | —— | 函数体内再做二次赋值(拷贝/移动赋值) |
-
列表发生在“构造函数体”之前;
-
函数体里看到的成员已经是“活”的对象,只能赋值,不能再次构造。
2. 效率:一次 vs 两次
表格
复制
场景 | 列表初始化 | 体中赋值 | 节约 |
---|---|---|---|
类类型成员(如 std::string ) |
直接构造 | 默认构造 + 赋值运算符 | 一次构造 |
重型对象(容器、锁、文件句柄…) | 同上 | 同上 | 显著 |
内置类型(int 、指针) |
无差别 | 无差别 | 无 |
结论:对非内置类型,列表几乎总是更快。
3. 功能:有些事“只能”在列表里做
表格
复制
需求 | 列表 | 函数体 |
---|---|---|
给引用成员绑定目标 | ✅ 必须 | ❌ 编译失败 |
给const 成员赋初值 | ✅ 必须 | ❌ 编译失败 |
显式调用基类非默认构造函数 | ✅ 必须 | ❌ 编译失败 |
把参数完美转发给成员 | ✅ 直接 | ❌ 需二次赋值 |
4. 顺序细节:与列表书写顺序无关
成员初始化严格按类中声明顺序进行,与初始化列表出现的先后无关。若依赖顺序,必须自己在声明时排好。
5. 代码对比示例
cpp
复制
class Foo {
int a_;
std::string s_;
public:
// 成员初始化列表:一次构造
Foo(int x, const char* str) : a_(x), s_(str) {}
// 普通构造函数体:默认构造 + 赋值
Foo(int x, const char* str) {
a_ = x; // 内置类型,无差别
s_ = str; // string 已默认构造,再调用 operator=
}
};
6. 一句话总结
-
成员初始化列表:在成员“出生”瞬间直接赋予初值,高效且能解决引用/
const
/基类等“先天需求”。 -
函数体赋值:成员先默认出生,再二次穿戴,效率低,且对某些成员干脆不可用。
因此,现代 C++ 共识:能用初始化列表就优先用列表;函数体只负责“出生后”的复杂逻辑。
全部评论