成员初始化列表构造函数,和普通构造函数有什么区别?

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++ 共识:能用初始化列表就优先用列表;函数体只负责“出生后”的复杂逻辑。

全部评论

·