【问题标题】:Is the order of initialization guaranteed by the standard?标准是否保证了初始化的顺序?
【发布时间】:2009-11-21 01:29:40
【问题描述】:

在下面的代码中,sn-p d1 的初始化器传递给了尚未构造的 d2(对吗?),那么 D 的复制构造器中的 d.j 是未初始化的内存访问吗?

struct D
{
    int j;

    D(const D& d) { j = d.j; }
    D(int i) { j = i; }
};

struct A
{
    D d1, d2;
    A() : d2(2), d1(d2) {}
};

C++ 标准的哪一部分讨论了数据成员的初始化顺序?

【问题讨论】:

标签: c++


【解决方案1】:

我现在手头没有标准,所以我不能引用该部分,但结构或类成员初始化总是按声明的顺序发生。构造函数初始化程序列表中提及成员的顺序无关。

Gcc 有一个警告-Wreorder 会在顺序不同时发出警告:

-Wreorder(仅限 C++) 当代码中给出的成员初始值设定项的顺序执行时发出警告 与它们必须执行的顺序不匹配。例如: 结构 A { 诠释我; 诠释 j; A(): j (0), i (1) { } }; 编译器会将 i 和 j 的成员初始化器重新排列为 匹配成员的声明顺序,发出警告 那个效果。此警告由 -Wall 启用。

【讨论】:

  • 这样做的原因是事物的破坏顺序与它们构造的相反。你不能改变在析构函数之后被破坏的顺序成员,因此构造的顺序必须对应。
  • 但是,您仍然可以引用以前初始化的成员,如下所述:parashift.com/c++-faq-lite/ctors.html#faq-10.7
  • 我认为这种情况下的FAQ解释非常有用;标准说“每个基和成员初始化后都有一个序列点”(12.6.2/3)。
【解决方案2】:

C++ 标准(ISO/IEC 14882:2003 12.6.2/5,初始化基和成员)说:

初始化应按以下顺序进行:

——首先,并且仅对于如下所述的最派生类的构造函数,虚拟基类应按照它们在基类的有向无环图的深度优先从左到右遍历中出现的顺序进行初始化,其中“从左到右”是派生类中基类名称的出现顺序 类基说明符列表。

——然后​​,直接基类应按照它们出现在 base-specifier-list 中的声明顺序进行初始化(不管 mem-initializers 的顺序如何)。

——然后​​,非静态数据成员应按照它们在类定义中声明的顺序进行初始化(同样不管 mem-initializers 的顺序)。

——最后,构造函数的主体被执行。

要点3保证非静态数据成员初始化的顺序。

【讨论】:

    【解决方案3】:

    在你的例子中它会失败:

    struct A
    {
        D d1, d2;    
        A() : d2(2), d1(d2) {}
    };
    
    d1: is initialised first as it is declared first.
    d2: is then initialized.
    

    因此,初始化列表将使用对无效对象 (d2) 的引用来构造 d1。

    这是将编译器警告级别调到尽可能高的原因之一。
    并且还强制它将所有警告报告为错误。

    【讨论】:

      【解决方案4】:

      Meyer 的Effective C++ 的第 13 条解释/强调了这种现象。它说析构函数必须以其构造函数的相反顺序销毁元素,因此所有构造函数必须以相同的顺序初始化元素,因此它们按照它们被声明的顺序初始化它们(而不是初始化列表的顺序) .

      【讨论】:

        【解决方案5】:

        是的。一个好的编译器应该警告你A::d2 将在A::d1 之后被初始化。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2020-09-11
          • 2016-04-16
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2021-04-05
          相关资源
          最近更新 更多