关于C++继承构造函数初始化行为的标准表述疑问
struct B1 { B1(int i): a(i) { std::cout << "B1" << std::endl; }; int a; }; struct B2 { int b; B2() { std::cout << "B2" << std::endl; } }; struct D1 : B2, B1 { using B1::B1; }; int main(void){ D1 d = D1(1); std::cout << d.a << '\n'; // 输出1 std::cout << d.b; // 值未定义 }
在这个示例中,D1通过继承B1的构造函数完成B1子对象的初始化,而B2子对象则会被默认构造函数初始化。这一行为符合C++标准中关于继承构造函数初始化的条款:
当调用类型B的构造函数初始化不同类型D的对象时(即构造函数被继承时),初始化过程如同使用默认构造函数初始化D对象以及每个从中继承构造函数的基类子对象,但B子对象由继承的构造函数初始化[...] 继承构造函数的参数初始化被排在D对象任何部分的初始化之前。
针对你提出的疑问——为何标准提及“构造函数的参数”而非“成员初始化列表”,可以从以下几点理解:
参数初始化是构造执行的前置步骤:
构造函数的参数初始化(包括实参绑定到形参、临时对象的创建等)是构造流程的第一步,完全独立于成员初始化列表。对于继承构造函数而言,它的核心是转发基类构造函数的调用,因此必须先完成参数的准备工作,才能将参数传递给基类构造函数。继承构造函数没有自身的成员初始化列表:
继承构造函数并非派生类自己定义的构造函数,它只是复用基类的构造逻辑,不存在属于派生类的成员初始化列表。标准强调参数初始化的顺序,是为了明确:即便派生类还有其他需要默认初始化的基类(比如示例中的B2)或成员,继承构造函数的参数准备也会优先于所有派生类对象部分的初始化操作。参数初始化与成员初始化列表的执行顺序不冲突:
在常规构造函数中,参数初始化完成后才会执行成员初始化列表,但继承构造函数的特殊性在于,它直接触发基类构造函数的调用,没有自己的初始化列表逻辑。标准的表述是为了明确优先级:参数的绑定与准备是最先发生的,之后才会依次处理派生类的其他基类初始化、成员初始化,最后执行基类构造函数的函数体。
内容的提问来源于stack exchange,提问作者Mia

