问题的历史:
初始化列表在 C++ 中出现,以优化和正确初始化类成员,在执行构造函数的主体之前。这是由 C 和 C++ 的经验导致的:在构造函数主体中初始化常量成员和引用是不可能的,只能在列表中进行。
问题:
尝试在构造函数主体中初始化这样的成员而不是使用初始化列表会导致编译错误。此外,忽略列表会导致成员对象的双重初始化调用,这会影响性能,特别是在复杂的初始化构造函数中。
解决方案:
最好通过初始化列表声明和初始化类成员——特别是常量 (const) 或引用 (&) 时。初始化列表在构造函数主体执行之前被使用,这时类成员还刚刚被构造。
代码示例:
class Example { const int value; int& ref; public: Example(int v, int& r) : value(v), ref(r) { /* 构造函数主体 */ } };
关键特性:
如果改变类成员的顺序和它们在列表中的初始化顺序会怎样?
初始化总是按类成员的声明顺序进行,而不是按初始化列表中的顺序进行。如果依赖的成员“不是按同样的顺序”初始化,可能会访问未初始化的内存。
代码示例:
class Foo { int x; int y; Foo() : y(2), x(y) {} // x 将首先初始化,值为未初始化的 y }
能否在构造函数主体中初始化 const 成员?
不可以。这将导致编译错误。只能通过初始化列表进行初始化。
如果不初始化引用成员会发生什么?
如果不初始化引用成员,会发生编译错误,因为引用在创建时必须“绑定”到对象,并且之后不能更改。
在用于存储设置的类中使用 const std::string 和引用,试图在构造函数主体中初始化这些成员,编译器会报错或数据未被初始化。
优点: 新手学会了区分构造和赋值错误。
缺点: 编译错误,无法使用类,可能在对象初始化阶段出现意外错误。
常量和引用通过初始化列表正确初始化。所有复杂的初始化代码集中在列表中,这提高了可读性并防止了错误。
优点: 安全且正确地初始化类成员,高可读性,没有泄漏或未定义行为。
缺点: 可能出现复杂的初始化逻辑,其中一些不容易在没有额外代码的情况下进行测试。