g++ -Wall选项包括-Wreorder。此选项的作用如下所述。这对我来说并不明显,为什么有人会关心(特别是足以在-Wall中默认打开这个)。
-Wreorder (C++ only)
Warn when the order of member initializers given in the code does not
match the order in which they must be executed. For instance:
struct A {
int i;
int j;
A(): j (0), i (1) { }
};
The compiler will rearrange the member initializers for i and j to
match the declaration order of the members, emit-ting a warning to that
effect. This warning is enabled by -Wall.
6条答案
按热度按时间8iwquhpp1#
考虑:
现在
i
被初始化为某个未知值,而不是零。或者,
i
的初始化可能会产生一些副作用,对于这些副作用,顺序很重要。例如pxy2qtax2#
问题是,有人可能会看到构造函数中的成员初始化器列表,并认为它们是按顺序执行的(首先是j,然后是i)。它们不是,它们是按照类中成员的定义顺序执行的。
假设你写了
A(): j(0), i(j) {}
。有些人可能会读到,并认为i最终的值为0。它没有,因为你用j初始化了它,它包含垃圾,因为它本身没有被初始化。警告提醒您编写
A(): i(j), j(0) {}
,希望它看起来更可疑。tkqqtvp13#
其他答案提供了一些很好的例子来证明警告的选择是正确的。我想我会提供一些历史背景。C++的创造者Bjarne Stroustrup在他的书The C++ programming language(第3版,第259页)中解释说:
成员的构造函数在包含类自己的构造函数体执行之前被调用。构造函数是按照它们在类中声明的顺序调用的,而不是按照它们在初始化器列表中出现的顺序。为了避免混淆,最好按声明顺序指定初始值设定项。成员析构函数按与构造相反的顺序调用。
jbose2ul4#
如果你的初始化器有副作用的话,这可能会对你不利。考虑:
上面的代码将打印“bar”然后是“foo”,尽管直觉上人们会假设顺序与初始化器列表中所写的一样。
或者,如果
x
和y
是具有构造函数的用户定义类型,则该构造函数也可能具有副作用,并具有相同的非显而易见的结果。当一个成员的初始化器引用另一个成员时,它也可以表现出来。
kqhtkvqz5#
该警告存在的原因是,如果您只是读取构造函数,则看起来
j
在i
之前被初始化。如果其中一个被用来初始化另一个,这就成了一个问题,如当你只看构造函数时,它看起来是安全的。但实际上,
j
在用于初始化i
时尚未初始化,因此代码不会按预期工作。所以才有警告。hzbexzde6#
已经有很多很好的答案了,但是还有一件事没有解决-- * 为什么 * 初始化的顺序是这样的?
类成员的初始化顺序是不是初始化器在构造函数中列出的顺序,而是它们在类本身中声明的顺序。原因有点微妙。
C保证成员变量的销毁顺序与构造顺序相反。由于一个类可以有多个构造函数,它们具有不同的初始化列表,析构函数需要知道顺序,这将是一个很大的簿记问题,并引入不必要的开销。解决方案是强制每个构造函数对成员使用相同的初始化顺序,以便析构顺序也可以固定。
由于不能使用初始化器顺序,唯一的选择是使用声明顺序。
虽然C以不同于初始化器列表的顺序初始化变量是完全法律的的,但这是可能的错误的非直观来源。警告让您知道要查找这些bug,或者重新排序初始化器列表以反映实际情况。