80

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

例子:

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

4

6 回答 6

114

原因是因为它们是按照它们在类中声明的顺序进行初始化的,而不是您在构造函数中初始化它们的顺序,并且它警告您不会使用构造函数的顺序。

这是为了帮助防止初始化b依赖的错误,a反之亦然。

这种排序的原因是因为只有一个析构函数,它必须选择一个“相反的顺序”来销毁类成员。在这种情况下,最简单的解决方案是使用类中的声明顺序来确保始终以正确的相反顺序销毁属性。

于 2012-08-31T21:09:46.363 回答
50

为什么要按照声明的顺序初始化成员变量?

无论您是否愿意,成员都将按照声明它们的顺序进行初始化。警告告诉您,您要求的顺序与初始化的实际执行顺序不同。

于 2012-08-31T21:14:52.037 回答
40

您不应该这样做,因为它会降低可读性并且可能具有误导性。

如果你这样做了:

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

看起来bthena都设置为1,而实际上未初始化的值b用于初始化a之前b被初始化为1

于 2012-08-31T21:04:39.230 回答
15

实际上编译器总是按照声明的顺序初始化变量,即使你以不同的顺序编写初始化程序。因此,如果您不按声明顺序编写初始化,则初始化程序的顺序不符合初始化顺序,如果初始化相互依赖,可能会导致细微的错误。

例如,考虑代码

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

这是一个错误,因为a之前已初始化b,但看起来没问题。如果按照声明的顺序(也就是初始化的顺序)来写,bug就很明显了:

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

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

于 2012-08-31T21:08:53.800 回答
5

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

- 墙只是一个开始。与名称所暗示的相反,-Wall 不会启用所有警告。有一些警告可以说是“过度”,但这些正是 -Wall 不启用的警告。我总是使用 -Wall 和其他人。

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

于 2012-08-31T21:37:37.397 回答
0

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

于 2019-09-06T11:50:04.053 回答