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

Rei*_*ica 60 c++ g++ compiler-warnings

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

例:

class Test {
    int a;
    int b;

public:
    Test() : b(1), a(2) {
    }
};

int main() {
    Test test;
    return 0;
}
Run Code Online (Sandbox Code Playgroud)

然后,如果我编译它-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
Run Code Online (Sandbox Code Playgroud)

我意识到这-Wall是明确要求GCC过度警告,但我认为所有这些都是有原因的.那么,初始化成员变量的顺序怎么样呢?

Mar*_*k B 87

原因是因为它们是按照它们在类中声明的顺序初始化的,而不是在构造函数中初始化它们的顺序,它警告你不会使用构造函数的顺序.

这有助于防止初始化b依赖的错误,a反之亦然.

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


Dav*_*eas 45

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

无论您是否需要,成员都将按照声明的相同顺序进行初始化.警告告诉您,您要求的订单与初始化的实际执行顺序不同.

  • @XavierHolt:在语言中,对于除动态分配的对象以外的所有对象,构造和销毁的顺序相反.如果初始化程序列表确定了初始化顺序,则不会定义具有多个构造函数的类中的初始化顺序,也不会定义成员的销毁顺序.构造/破坏顺序颠倒的原因是为了确保如果一个物体依赖于另一个物体,那么第二个物体将不会在第一个物体之前被销毁. (5认同)
  • 对于不使用"编译器"一词的简洁,最直接的答案+1. (4认同)

CB *_*ley 34

你不应该因为它降低了可读性而且可能会产生误导.

如果你这样做:

Test() : b(1), a(b) {}
Run Code Online (Sandbox Code Playgroud)

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

  • 示例中的+1表示为什么订单真正重要以及如果有人假设排序可能会出错,这是他们在构造函数中声明的内容. (5认同)

cel*_*chk 14

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

例如,考虑代码

Test(): b(42), a(b) {}
Run Code Online (Sandbox Code Playgroud)

这是一个错误,因为a之前已初始化b,但它看起来不错.如果你按声明的顺序(这是初始化的顺序)写它,那么bug就很明显了:

Test(): a(b), b(42) {}
Run Code Online (Sandbox Code Playgroud)

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


Dav*_*men 5

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

-Wall只是一个开始.与名称所暗示的相反,-Wall不会启用所有警告.有一些警告可以说是"超过顶部",但这些警告正是-Wall无法启用的警告.我总是使用-Wall和其他人.

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