【问题标题】:Does the order of base-class initializers and member variable initializers matter?基类初始化器和成员变量初始化器的顺序是否重要?
【发布时间】:2012-01-13 17:03:33
【问题描述】:

类的构造函数的初始化顺序重要吗?

所以说我有:

class MyClass : BaseClass
{
      int a, b, c;

   public:
      MyClass(int);
}

例如1:

MyClass::MyClass(int forBase) :
  a(7),
  b(14),
  c(28),
  BaseClass(forBase) { }

例如2:

MyClass::MyClass(int forBase) :
  BaseClass(forBase),
  a(7),
  b(14),
  c(28) { }

示例 1 会与示例 2 做一些不同的事情吗?

【问题讨论】:

  • 我对第三种情况感兴趣。假设没有基类。初始化abc 的顺序是否重要? g++ 使用-Weffc++ 对此发出警告(我认为)。
  • 不,不是,重要的是声明的顺序,查看this,有一个C++03标准的引用,我认为C+没有什么不同+11。
  • @Mr.TAMER:你发现了一个重复
  • @LightnessRacesinOrbit:我知道,并且打算声明,但我认为不要这样做,因为我认为有人会发布新标准的报价,所以答案会比这更好,我碰巧:) 所以我真的认为在那个答案中添加一个指向这个答案的链接会非常好:)
  • 仅供参考,[C++03: 12.6.2/5] 的措辞相同。 :)

标签: c++ constructor initialization


【解决方案1】:

示例 1 会与示例 2 做一些不同的事情吗?

。初始化顺序由标准决定,而不是由您编写初始化程序的顺序决定:

[C++11: 12.6.2/10]: 在非委托构造函数中,初始化 按以下顺序进行:

  • 首先,并且仅对于最派生类 (1.8) 的构造函数,虚拟基类按照它们在基类的有向无环图的深度优先从左到右遍历中出现的顺序进行初始化,其中“从左到右”是派生类 base-specifier-list 中基类的出现顺序。
  • 然后,直接基类按照它们出现在 base-specifier-list 中的声明顺序进行初始化(无论 mem-initializer 的顺序如何)
  • 然后,非静态数据成员按照它们在类定义中声明的顺序进行初始化(同样与 mem-initializers 的顺序无关)
  • 最后,构造器主体的复合语句被执行。

事实上,如果你以任何其他顺序编写它们并且一个依赖于另一个,you may well be warned about it

struct T {
   std::vector<int> v;
   int w;

   T(int w) : w(w), v(0, w) {}
};

int main() {
   T t(3);
}

// g++ 4.1.2:
// t.cpp: In constructor 'T::T(int)':
// Line 3: warning: 'T::w' will be initialized after
// Line 2: warning:   '__gnu_debug_def::vector<int, std::allocator<int> > T::v'
// Line 5: warning:   when initialized here

【讨论】:

【解决方案2】:

顺序对编译器无关紧要(初始化顺序始终是基类在前,基类始终按派生顺序,成员按声明顺序),但对读者来说很重要:它是如果您给初始化程序的顺序与它们的执行顺序不匹配,那将非常令人困惑。虽然在大多数情况下这无关紧要,但在某些情况下,您可以创建细微的错误,例如

struct Derived: Base
{
  int member;
  Derived();
}

Derived::Derived():
  member(3),
  Base(member) // This is executed *before* member is initialized!
{
}

如果初始化器的顺序正确,这个错误会更明显:

Derived::Derived():
  Base(member), // Now we see immediately that member is uninitialized
  member(3),
{
}

【讨论】:

  • 如果初始化列表的顺序错误,一些编译器会发出更高级别的警告。
  • @MooingDuck:是的。但如果您不知道这可能导致的问题,您可能倾向于忽略或禁用该警告。
  • 如果您忽略并禁用您不理解的警告,那么无论如何您都注定要失败。
  • @LightnessRacesinOrbit:当然。但是您可能认为您理解它(毕竟您理解它所说的),但并不真正理解问题所在(即您可能理解初始化的顺序不依赖于初始化程序的顺序,但可能认为它不无论如何都无所谓——这是一个额外的步骤,可以让您更难发现错误)。
  • 这仍然是一种愚蠢的编程方式。警告表示您理解某些内容(或者您犯了可以识别的错误)。
【解决方案3】:

在构造函数初始化列表中列出初始化程序的顺序无关紧要。成员按照声明的顺序进行初始化,基类在成员之前初始化。

但是,如果子对象的初始值依赖于其他子对象的值,则以不同的顺序列出初始化程序可能会让您感到厌烦。

class A
{
  int y, x;
  A(int x_value): x(x_value), y(x) {}
};

由于 y 在 x 之前初始化,它得到一个垃圾值,而初始化列表的顺序正好隐藏了 bug。这就是为什么这值得编译器警告。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2023-03-24
    • 1970-01-01
    • 1970-01-01
    • 2016-06-23
    • 2015-09-25
    • 1970-01-01
    • 2016-08-19
    • 1970-01-01
    相关资源
    最近更新 更多