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
};
上面只是伪代码。实际上我想知道调用基础构造函数的顺序是否重要? 是否有任何不良行为(特别是极端情况)由任何案件引起?我的问题是关于编码风格的更多技术方面和不。
答案 0 :(得分:16)
您在问题中提到的顺序不是“调用基础构造函数的顺序”。实际上,你不能调用构造函数。构造函数不能由用户调用。只有编译器才能调用构造函数。
您可以做的是指定初始值设定项。在这种情况下(构造函数初始化列表),您要为某个较大对象的子对象指定初始值设定项。指定这些初始值设定项的顺序无关紧要:编译器将按照语言规范定义的非常特定的顺序调用构造函数,而不管指定初始值设定项的顺序如何。始终首先调用基类构造函数(按照类定义中列出基类的顺序),然后调用成员子对象的构造函数(同样,按照类定义中列出这些成员的顺序)。
(这个规则在虚拟基类方面有一些特点,但我决定不在这里包含它们。)
至于不良行为......当然,这里存在“不良行为”的可能性。如果你假设初始化的顺序取决于你在构造函数初始化程序列表中使用的顺序,当你发现编译器完全忽略该顺序并使用它自己的顺序时,你很可能最终会遇到令人不快的意外(顺序)声明)而不是。例如,此代码的作者
struct S {
int b, a;
S() : a(5), b(a) {}
};
可能希望a
首先被初始化,而b
可以从5
接收a
的初始值,但实际上这不会发生,因为{{ 1}}在b
之前初始化。
答案 1 :(得分:6)
订单定义明确。它不取决于您在初始化时如何指定它们
将首先调用基类构造函数B
,然后按成员变量(d1
& d2
)的顺序调用它们。
解释@Andrey T答案中的评论。
class MyClass1: public MyClass2, public virtual MyClass3
{
};
调用Base类构造函数的顺序由标准很好地定义,并且将是:
MyClass3
MyClass2
MyClass1
虚拟基类MyClass3
优先于基类MyClass2
。
答案 2 :(得分:3)
初始化列表中出现的订单不重要。在您的情况下,将始终首先初始化基础对象,然后按顺序初始化d1和d2。初始化按推导顺序执行,并按顺序出现在类定义中。
话虽如此,按初始化顺序编写初始化列表通常被认为是好的风格,如果你不这样做,一些编译器会发出警告。