为什么我应该按照声明的顺序初始化成员variables?
我今天正在编写一些代码,并且出现了一个奇怪的编译错误,这个错误似乎是由于以不同的顺序初始化成员variables而导致的。
例:
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
明确要求海湾合作委员会警告,但我认为这是所有的理由。 那么,如何初始化成员variables的顺序呢?
原因是因为它们是按照它们在你的类中声明的顺序进行初始化的,而不是在构造函数中初始化它们的顺序,并且警告你不会使用构造函数的顺序。
这是为了防止b
初始化依赖于a
错误,反之亦然。
这个顺序的原因是因为只有一个析构函数,它必须select一个“反向顺序”来销毁类成员。 在这种情况下,最简单的解决scheme是使用类中声明的顺序来确保属性总是以正确的相反顺序销毁。
为什么我应该按照声明的顺序初始化成员variables?
无论您是否愿意,成员都将按照与其声明相同的顺序进行初始化。 警告告诉你,你要求的顺序与初始化的实际顺序不同。
你不应该这样做,因为它会降低可读性,并有可能产生误导。
如果你这样做:
Test() : b(1), a(b) {}
那么看起来b
然后a
被设置为1
,而实际上b
的未初始化值被用来初始化b
之前被初始化为1
。
实际上编译器总是按照声明的顺序初始化variables,即使你以不同的顺序编写初始化器。 因此,如果不按照声明的顺序编写初始化,初始化的顺序不适合初始化的顺序,如果初始化彼此依赖,可能会导致微妙的错误。
例如,考虑代码
Test(): b(42), a(b) {}
这是一个错误,因为a
在b
之前被初始化,但是看起来没问题 。 如果你按照声明的顺序(这是初始化的顺序)编写它,这个错误就会变得很明显:
Test(): a(b), b(42) {}
请注意,该错误也可以比这更微妙; 例如,想象a
和b
是在构造函数中输出某些东西的类types; 那么以“错误”的顺序,你会认为b
的输出应该出现在a
之前,实际上会发生相反的情况。 如果首先出现的输出会导致无效的文件,那也是一个错误,但是如果构造函数在另一个翻译单元中,编译器不可能注意到问题(除了编译器不知道重新sorting是否或者不是一个错误)。 因此,编译器只是警告每个不匹配顺序的实例是合理的。
我意识到 – 墙明确要求海湾合作委员会警告,但我认为这是所有的理由。
– 墙只是一个开始。 与名称暗示的相反,“墙”不启用所有警告。 有一些警告可以说是“过顶”的,但那些警告恰恰是 – 墙不能启用的。 我总是使用-Wall加上其他人。
至于你的投诉,正如其他人已经指出的那样,这个警告有很好的理由。 仅仅因为您指定了一个订单并不意味着编译器将使用该订单。 编译器必须使用的顺序是基于类定义的。