为什么我应该按照它们声明的顺序初始化成员变量?

时间:2012-08-31 21:01:20

标签: c++ g++ compiler-warnings

我今天编写了一些代码并得到了一个奇怪的编译错误,这似乎是由于成员变量的初始化顺序与声明的顺序不同。

示例:

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过度警告,但我认为所有这些都是有原因的。那么,初始化成员变量的顺序怎么样呢?

6 个答案:

答案 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) {}

这是一个错误,因为ab之前已初始化,但看起来确定。如果你按声明的顺序(这是初始化的顺序)写它,那么bug就显而易见了:

Test(): a(b), b(42) {}

请注意,错误也可能比这更微妙;例如,imag ab是在其构造函数中输出内容的类类型;然后使用“不正确”的顺序,您认为b的输出应该出现在a之前,而实际上反之则会发生。如果首先出现a的输出将导致文件无效,那也是一个错误,但如果构造函数在另一个转换单元中,编译器就无法注意到问题(除了编译器不能知道重新排序是否是一个错误)。因此,编译器只是警告每个不匹配顺序的实例是合理的。

答案 4 :(得分:5)

  

我意识到-Wall明确要求GCC过度警告,但我认为所有这些都是有原因的。

-Wall只是一个开始。与名称所暗示的相反,-Wall不会启用所有警告。有一些警告可以说是“超过顶部”,但这些警告正是-Wall无法启用的警告。我总是使用-Wall plus others。

至于你的投诉,正如其他人已经注意到的那样,这个警告是有充分理由的。仅仅因为您指定订单并不意味着编译器将使用该订单。编译器必须按照标准使用的顺序基于类定义。

答案 5 :(得分:0)

Initializer列表提供了以任意顺序初始化变量的灵活性。但是,实际的初始化根据您声明类成员的顺序进行。为避免此警告,您必须重新排序初始化/声明。