【问题标题】:Can member variables be used to initialize other members in an initialization list?成员变量可以用来初始化初始化列表中的其他成员吗?
【发布时间】:2019-10-23 03:09:59
【问题描述】:

考虑以下(简化的)情况:

class Foo
{
private:
    int evenA;
    int evenB;
    int evenSum;
public:
    Foo(int a, int b) : evenA(a-(a%2)), evenB(b-(b%2)), evenSum(evenA+evenB)
    {
    }
};

当我像这样实例化 Foo 时:

Foo foo(1,3);

那么evenA为0,evenB为2,但是evenSum会被初始化为2吗?

我在我当前的平台 (iOS) 上尝试过,它似乎可以工作,但我不确定这段代码是否可移植。

感谢您的帮助!

【问题讨论】:

  • 这是 C++ 中的危险角落之一。
  • 键盘是查看此类内容的好地方:codepad.org/uFgZpkwN
  • @Agent_L:这不会告诉你代码是否可移植。
  • @OliCharlesworth 没有,但有时它会告诉你是否不是。

标签: c++ initialization-list member-variables


【解决方案1】:

这个定义明确且可移植,1但它可能容易出错。

成员按照它们在类主体中声明的顺序进行初始化,而不是它们在初始化列表中列出的顺序。因此,如果您更改类主体,此代码可能会静默失败(尽管许多编译器会发现这一点并发出警告)。


1。来自 C++ 标准中的 [class.base.init]:

在非委托构造函数中,初始化按以下顺序进行:

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

(突出显示是我的。)

标准的这一部分接着给出一个使用成员变量来初始化其他成员变量的例子。

【讨论】:

  • 好的,所以如果 Foo 有一个基类 Bar(非虚拟公共继承)并且我将 Bars 构造函数放在初始化列表中,它将始终在所有成员初始化器之前执行,即使我把它放在初始化列表结束了吗?
  • @Pontomedon:是的。基类构造函数总是首先被调用(即使它们根本不在列表中)。
【解决方案2】:

是的,只要它们已经建成。只是不要忘记 构造的顺序是声明的顺序 类定义,不是初始化器在 构造函数。并且编译器通常不会告诉您是否使用 在构造之前的变量。例如,在您的情况下, 如果你将evenSum 移到类的顶部,你有 undefined 行为(因为它的初始化器使用未初始化的成员),甚至 虽然在你的构造函数中,你初始化 evenAevenB 词法 在evenSum 之前。

【讨论】:

    【解决方案3】:

    成员按照它们在类定义中声明的顺序进行初始化。只要你的初始化列表遵循这个顺序,应该没问题。

    【讨论】:

      【解决方案4】:

      这在 g++ 4.0.3(现在 6 岁)上也编译没有错误。

      我相信这在任何合理的最新编译器上都能正常编译。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2011-07-14
        • 1970-01-01
        • 2019-09-10
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多