我今天编写了一些代码并得到了一个奇怪的编译错误,这似乎是由于成员变量的初始化顺序与声明的顺序不同。
示例:
class Test {
int a;
int b;
public:
Test() : b(1), a(2) {
}
};
int main() {
Test test;
return 0;
}
然后如果我用-Werror -Wall
编译它:
$ g++ -Werror -Wall test.cpp
test.cpp: In constructor ‘Test::Test()’:
test.cpp:3:9: error: ‘Test::b’ will be initialized after [-Werror=reorder]
test.cpp:2:9: error: ‘int Test::a’ [-Werror=reorder]
test.cpp:6:5: error: when initialized here [-Werror=reorder]
cc1plus: all warnings being treated as errors
我意识到-Wall
明确要求GCC过度警告,但我认为所有这些都是有原因的。那么,初始化成员变量的顺序怎么样呢?
答案 0 :(得分:82)
原因是因为它们是按照它们在类中声明的顺序初始化的,而不是在构造函数中初始化它们的顺序,它警告你不会使用构造函数的顺序。
这有助于防止b
的初始化取决于a
或反之亦然的错误。
这种排序的原因是因为只有一个析构函数,它必须选择“逆序”来销毁类成员。在这种情况下,最简单的解决方案是使用类中的声明顺序,以确保始终以正确的相反顺序销毁属性。
答案 1 :(得分:41)
为什么我要按照它们声明的顺序初始化成员变量?
成员将按照声明的相同顺序进行初始化,无论您是否需要。警告告诉您,您要求的订单与初始化的实际执行顺序不同。
答案 2 :(得分:31)
你不应该因为它降低了可读性而且可能会产生误导。
如果你这样做了:
Test() : b(1), a(b) {}
似乎b
然后a
都设置为1
,而实际上b
的未初始化值用于在a
之前初始化b
{1}}已初始化为1
。
答案 3 :(得分:12)
实际上,编译器始终按声明顺序初始化变量,即使您以不同的顺序编写初始值设定项也是如此。因此,如果您不按声明的顺序编写初始化,则初始化程序的顺序与初始化顺序不一致,如果初始化相互依赖,则可能导致细微的错误。
例如,考虑代码
Test(): b(42), a(b) {}
这是一个错误,因为a
在b
之前已初始化,但看起来确定。如果你按声明的顺序(这是初始化的顺序)写它,那么bug就显而易见了:
Test(): a(b), b(42) {}
请注意,错误也可能比这更微妙;例如,imag a
和b
是在其构造函数中输出内容的类类型;然后使用“不正确”的顺序,您认为b
的输出应该出现在a
之前,而实际上反之则会发生。如果首先出现a
的输出将导致文件无效,那也是一个错误,但如果构造函数在另一个转换单元中,编译器就无法注意到问题(除了编译器不能知道重新排序是否是一个错误)。因此,编译器只是警告每个不匹配顺序的实例是合理的。
答案 4 :(得分:5)
我意识到-Wall明确要求GCC过度警告,但我认为所有这些都是有原因的。
-Wall只是一个开始。与名称所暗示的相反,-Wall不会启用所有警告。有一些警告可以说是“超过顶部”,但这些警告正是-Wall无法启用的警告。我总是使用-Wall plus others。
至于你的投诉,正如其他人已经注意到的那样,这个警告是有充分理由的。仅仅因为您指定订单并不意味着编译器将使用该订单。编译器必须按照标准使用的顺序基于类定义。
答案 5 :(得分:0)
Initializer列表提供了以任意顺序初始化变量的灵活性。但是,实际的初始化根据您声明类成员的顺序进行。为避免此警告,您必须重新排序初始化/声明。