168

g++ -Wall 选项包括 -Wreorder。该选项的作用如下所述。我不清楚为什么有人会关心(特别是足以在 -Wall 中默认打开它)。

-Wreorder(仅限 C++)
  当代码中给出的成员初始化器的顺序不正确时发出警告
  匹配它们必须执行的顺序。例如:

    结构 A {
      诠释我;
      诠释 j;
      A(): j (0), i (1) { }
    };

  编译器会将 i 和 j 的成员初始化器重新排列为
  匹配成员的声明顺序,发出警告
  影响。此警告由 -Wall 启用。
4

5 回答 5

280

考虑:

struct A {
    int i;
    int j;
    A() : j(0), i(j) { }
};

现在i被初始化为某个未知值,而不是零。

或者, 的初始化i可能会产生一些顺序很重要的副作用。例如

A(int n) : j(n++), i(n++) { }
于 2009-12-01T18:40:30.193 回答
49

问题是有人可能会在构造函数中看到成员初始化器列表,并认为它们是按该顺序执行的(首先是 j,然后是 i)。它们不是,它们按照成员在类中定义的顺序执行。

假设你写了A(): j(0), i(j) {}. 有人可能会读到它,并认为 i 以值 0 结束。它没有,因为你用 j 初始化它,它包含垃圾,因为它本身没有被初始化。

警告提醒你写A(): i(j), j(0) {},希望看起来更可疑。

于 2009-12-01T18:42:10.220 回答
21

其他答案提供了一些很好的例子来证明警告选项是合理的。我想我会提供一些历史背景。C++ 的创建者 Bjarne Stroustrup 在他的《C++ 编程语言》(第 3 版,第 259 页)一书中解释道:

在执行包含类自己的构造函数的主体之前调用成员的构造函数。构造函数的调用顺序是它们在类中的声明顺序,而不是它们在初始化列表中出现的顺序。为避免混淆,最好按声明顺序指定初始化程序。成员析构函数的调用顺序与构造的相反。

于 2013-02-16T12:44:52.140 回答
13

如果您的初始化程序有副作用,这可能会咬您一口。考虑:

int foo() {
    puts("foo");
    return 1;
}

int bar() {
    puts("bar");
    return 2;
}

struct baz {
    int x, y;
    baz() : y(foo()), x(bar()) {}
};

上面将打印“bar”然后是“foo”,尽管直觉上会假设 order 是写在初始化列表中的。

或者,如果xy是具有构造函数的用户定义类型,则该构造函数也可能具有副作用,具有相同的非显而易见的结果。

当一个成员的初始化程序引用另一个成员时,它也可以表现出来。

于 2009-12-01T18:40:59.733 回答
8

存在警告是因为如果您只是阅读构造函数,它看起来就像ji. 如果一个用于初始化另一个,这将成为一个问题,如

struct A {
  int i;
  int j;
  A(): j (0), i (this->j) { }
};

当您只查看构造函数时,这看起来很安全。但实际上,j在用于初始化的地方还没有初始化i,所以代码不会按预期工作。因此发出警告。

于 2009-12-01T18:42:15.330 回答