子对象初始化的顺序是什么?

3
假设我们有一个对象 o,它是某个类型的类,并包含另外两个类类型的成员子对象 so 和 sso。请考虑以下示例:
#include <iostream>
using namespace std;
    struct SO{ SO(){ cout << "SO()" << endl; } };
    struct SSO{ SSO(){ cout << "SSO()" << endl; } };

        struct O
        {
            O(){ cout << "O()" << endl; }
            SO so;
            SSO sso;
        };
    int main()
    {
        O o = *(new O);
    }

输出:

SO()

SSO()

O()

演示

5.3.4 部分 所述:

创建 T 类型对象的 new-expression 初始化该对象如下:

— 如果省略 new-initializer,则对象进行默认初始化 (8.5); 如果未执行任何初始化,对象的值是不确定的。

— 否则,new-initializer 根据 8.5 中直接初始化的初始化规则进行解释。

在这种情况下,对象 o 进行默认初始化(即构造函数调用)。但它的子对象呢?似乎也进行了默认初始化。但是标准中哪里指定默认初始化适用于任何子对象,如果它们的完整对象被默认初始化?


1
那个o的声明是立即泄漏内存的好方法。无论如何,你需要[class.base.init]。 - chris
3个回答

6
在对象o上执行默认初始化是不正确的。在您的示例中,对象o是复制初始化的。在您的示例中,未命名对象由new创建(随后泄漏),进行了默认初始化。
现在,您使用new创建的类型为O的未命名对象确实已经进行了默认初始化,这意味着它通过调用用户定义的默认构造函数O::O()进行了初始化。 O::O()构造函数中的构造函数初始化程序列表完全不存在,即没有提到任何子对象。这意味着这些子对象将被默认初始化。
如下所述: 12.6.2 初始化基类和成员 8、在非委托构造函数中,如果给定的非静态数据成员或基类没有被mem-initializer-id指定(包括构造函数没有ctor-initializermem-initializer-list的情况),并且该实体不是抽象类的虚基类(10.4),则
- 如果该实体是具有brace-or-equal-initializer的非静态数据成员,则按8.5中指定的方式初始化该实体; - 否则,如果该实体是变量成员(9.5),则不执行初始化; - 否则,对该实体进行默认初始化(8.5)。
最后一个选项适用于您的情况。(编号可能有误,因为我正在使用文件草案版本。)
请注意,您的问题标题提到了“子对象初始化顺序”,而实际问题与顺序本身无关。它涉及初始化方法。

这正是我一直在寻找的。谢谢。 - St.Antario

4

§12.6.2/10

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

  • 首先,仅对于最派生类(1.8)的构造函数,虚基类以深度优先从左到右遍历基类有向无环图的顺序进行初始化,其中“从左到右”是基类在派生类的base-specifier-list中出现的顺序。
  • 然后,直接基类按照它们在base-specifier-list中出现的声明顺序进行初始化(不考虑mem-initializers的顺序)。
  • 接着,非静态数据成员按照它们在类定义中声明的顺序进行初始化(同样不考虑mem-initializers的顺序)。
  • 最后,执行构造函数体的compound-statement

因此:无论选择哪个构造函数进行初始化(默认或非默认,显式默认或用户提供的),首先初始化虚基类,然后初始化直接基类(按照它们在base-specifier-list中出现的顺序,正如所述——而不是按照它们在ctor-initializer中出现的顺序);最后按照声明顺序初始化非静态数据成员子对象。


0
8.5/6 默认初始化类型为T的对象意味着:
  • 如果T是一个(可能带有cv限定符的)类类型(第9条),则调用T的默认构造函数...

1
这不是一个答案,因为我对子对象的初始化很感兴趣。 - St.Antario
默认构造函数当然会初始化对象的子对象。我本来打算讲到这个,但是@AndreyT比我先发了。 - Igor Tandetnik

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接