1.35、成员变量初始化的顺序是什么?

成员变量初始化的顺序

  1. 1. 基类子对象先于派生类子对象初始化
    • • 首先初始化虚基类(如果有),按照继承声明的顺序(深度优先、左到右)。
    • • 接着初始化直接基类,顺序与类声明中基类列表的顺序一致。
  2. 2. 成员变量初始化顺序由成员声明顺序决定
    • • 无论构造函数初始化列表中写的顺序如何,成员变量总是按照它们在类中声明的先后顺序依次初始化。
    • • 这意味着即使你在初始化列表中调换了顺序,实际初始化顺序不会改变,编译器会按照声明顺序执行。
  3. 3. 构造函数体内的赋值操作发生在成员初始化之后
    • • 如果成员没有在初始化列表中初始化,则会先调用默认构造函数(或进行默认初始化),然后在构造函数体内赋值。
    • • 这通常效率较低,且对于const成员和引用成员,必须在初始化列表中初始化。
  4. 4. 特殊成员的初始化要求
    • • const成员和引用成员必须在初始化列表中初始化,不能在构造函数体内赋值。
    • • static成员变量不属于对象实例,不在构造函数中初始化,必须在类外定义并初始化。

典型顺序总结

阶段 初始化顺序说明
静态变量 全局静态变量和类的静态成员变量先于对象初始化
虚基类 按继承图深度优先、左到右顺序初始化
直接基类 按声明顺序初始化
非静态数据成员 按声明顺序初始化
构造函数体 执行构造函数体内的语句

额外建议

  • • 初始化列表中成员顺序应与声明顺序一致,避免阅读误导和编译器警告。
  • • 避免成员间初始化依赖,防止因顺序错误导致未定义行为。
  • • 对于复杂依赖,考虑使用委托构造函数或工厂函数保证初始化安全。

参考示例

class Base {
public:
    Base() { /*...*/ }
};

class Derived : public Base {
    int a;
    int b;
public:
    Derived(int x, int y)
    : b(y), a(x) // 虽然b先写,但a会先初始化,因为a先声明
    {
        // 构造函数体
    }
};

在上例中,a会先于b初始化,因为它在类中先声明,初始化列表的顺序不会影响实际初始化顺序。

总结

成员变量初始化顺序由基类继承层次和成员声明顺序决定,与构造函数初始化列表顺序无关。理解这一点是编写健壮、无隐藏bug的C++类构造函数的基础。
(加入我的知识星球,免费获取账号,解锁所有文章。)
本文首发于【讳疾忌医-note】公众号,未经授权,不得转载。

阅读剩余
THE END