【问题标题】:Order of calling base class constructor from derived class initialization list从派生类初始化列表调用基类构造函数的顺序
【发布时间】:2011-09-09 00:12:22
【问题描述】:
struct B { 
  int b1, b2;  
  B(int, int);
};

struct D : B {
  int d1, d2;
// which is technically better ?
  D (int i, int j, int k, int l) : B(i,j), d1(k), d2(l) {} // 1st Base
// or
  D (int i, int j, int k, int l) : d1(k), d2(l), B(i,j) {} // last Base
};

以上只是一个伪代码。其实我想知道调用基础构造函数的顺序重要吗?

是否存在由任何案例引起的不良行为(尤其是极端案例)?我的问题是关于更多技术方面的问题,不是关于编码风格的。

【问题讨论】:

    标签: c++ inheritance constructor base-class initialization-list


    【解决方案1】:

    您在问题中引用的顺序不是“调用基本构造函数的顺序”。事实上,你不能调用构造函数。构造函数不能被用户调用。只有编译器可以调用构造函数。

    您可以做的是指定初始化程序。在这种情况下(构造函数初始化器列表),您正在为某个较大对象的子对象指定初始化器。指定这些初始化程序的顺序无关紧要:编译器将以语言规范定义的非常特定的顺序调用构造函数,而不管您指定初始化程序的顺序如何。总是首先调用基类构造函数(按照基类在类定义中列出的顺序),然​​后调用成员子对象的构造函数(同样,按照这些成员在类定义中列出的顺序)。

    (当涉及到虚拟基类时,这条规则有一些特殊性,但我决定不在这里包含它们。)

    至于不良行为……当然,这里也有“不良行为”的可能。如果您假设初始化的顺序取决于您在构造函数初始化器列表中使用的顺序,那么当您发现编译器完全忽略该顺序并使用它自己的顺序(声明)代替。比如这段代码的作者

    struct S {
      int b, a;
      S() : a(5), b(a) {}
    };
    

    可能希望首先初始化a,然后ba 接收5 的初始值,但实际上这不会发生,因为ba 之前初始化。

    【讨论】:

    • 可能,还值得一提的是,虚拟基类按照基类的初始化顺序优先。
    • @Als: ... 并且虚拟基类初始化器被简单地忽略,除非您正在构造最派生的对象。我不想在这些问题上过多地回答我的问题。
    • "构造函数不能被用户调用。只有编译器可以调用构造函数。"不是用placement new 手动调用构造函数吗?还是这被认为是“运行”构造函数?
    【解决方案2】:

    顺序定义明确。它不取决于您在初始化时如何指定它们。
    将首先调用基类构造函数B,然后按声明顺序调用成员变量(d1 & d2)。

    解释@Andrey T 回答中的评论。

    class MyClass1: public MyClass2, public virtual MyClass3
    {
    
    
    };
    

    标准对基类构造函数的调用顺序进行了很好的定义,将是:

    MyClass3  
    MyClass2
    MyClass1
    

    虚拟基类MyClass3 优先于基类MyClass2

    【讨论】:

    • 您能在答案中解释您的virtual 基本评论吗?
    • @aloksave 为什么虚拟基类优先于 myclass2。
    • @darth_coder virtual 基类被优先考虑,因为它们必须在任何依赖它们的非virtual 基类之前构造。考虑一下:struct A{}; struct B : virtual A {}; struct C: virtual A {}; struct D : A, B {};。在这种情况下,D 必须先构造A,然后才能构造BC,因为BC 都要求先构造A。而不是允许BC 构造A(在更复杂的情况下可能会变得复杂,例如如果还有struct E : C, B {};,则需要额外的编译器
    • 开销并可能引入未定义的行为(如果一个简单的编译器决定B 总是构造A,那么E 将有UB,因为它的C 是在它之前构造的B 可以构造它的A)或者打破预期(如果说编译器默默地,邪恶地在C 之前构造B,忽略E : C, B,那么这违反了最小惊讶原则)),标准简单指定最派生类在构造任何非virtual 基类之前必须在其继承层次结构中构造任何virtual 基类,保证
    • A 在构造 BC 时将始终有效,无论它们以何种顺序构造,防止任何 UB。 (或者,为了缩短文本墙,virtual 基类优先保证它们在构造非virtual 基类时始终有效,从而防止 UB 和/或防止编译器违反防止 UB 的标准。)
    【解决方案3】:

    初始化列表中出现的顺序并不重要。在您的情况下,基础对象将始终首先初始化,然后按顺序初始化 d1 和 d2。初始化是按照派生顺序和成员出现在类定义中的顺序执行的。

    话虽如此,通常认为按照初始化顺序编写初始化列表是一种很好的风格,如果你不这样做,一些编译器会发出警告。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2023-03-31
      • 1970-01-01
      • 2012-05-19
      • 2020-07-06
      • 2016-07-19
      • 1970-01-01
      • 2011-05-03
      相关资源
      最近更新 更多