10
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
};

上面只是一个伪代码。实际上,我想知道调用基本构造函数的顺序是否重要?

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

4

3 回答 3

18

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

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

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

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

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

可能期望a首先被初始化,并接收fromb的初始值,但实际上这不会发生,因为它是在之前初始化的。5aba

于 2011-06-06T03:58:33.007 回答
6

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

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

class MyClass1: public MyClass2, public virtual MyClass3
{


};

调用基类构造函数的顺序由标准很好地定义,将是:

MyClass3  
MyClass2
MyClass1

虚拟基类MyClass3优先于基类MyClass2

于 2011-06-06T03:55:51.573 回答
3

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

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

于 2011-06-06T03:55:38.503 回答