在初始化列表中引用类成员是不确定的行为?

时间:2016-12-24 20:21:36

标签: c++

假设我有以下内容:

Invoke-RemoteScript

B要求在其构造函数上提供对C的引用。如果A类提供C,那么将A的初始化列表指定为以下内容是合法的......

class A {
   B member1;
   C member2;
public:
   A();
};

class B {
public:
   C& ref_to_c;
   B( C& ref_to_c );
};

class C {
 ...
};

也就是说,member2是否存在于初始化列表阶段,还是未定义的行为

2 个答案:

答案 0 :(得分:14)

初始化如下:

  

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

     

- 首先,仅适用于派生程度最高的类的构造函数   如下所述,虚拟基类应在中初始化   命令它们出现在深度优先从左到右的遍历中   基类的有向非循环图,其中“从左到右”是   派生类中基类名称的出现顺序   基说明符列表。

     

- 然后,直接基类应按声明顺序初始化   因为它们出现在base-specifier-list中(无论顺序如何)   mem-initializers)。

     

- 然后,非顺序数据成员应按顺序初始化   它们在类定义中被声明(再次无论如何   记忆初始化者的顺序。

     

- 最后,执行构造函数的主体。 [注意:   声明命令是强制要求确保基础和成员   子对象以初始化的相反顺序销毁。 ]

这基本上意味着member1将始终在member2之前初始化。所以,B的构造函数将首先运行。 即使你在A的构造函数中明确地以相反的顺序调用它们:

A() : member2(foo), member1(bar) {}

没有有所作为。现在,引用未初始化的对象本身不是UB,但它可以取决于B的构造函数。你应该改变声明的顺序:

C member2;
B member1;

答案 1 :(得分:4)

您正在组建member1,其中包含对memebr2的引用。 这还没有构建,但编译器已经知道它将在哪里(因此它可以提供引用)。

它会起作用,但如果您尝试在B ctor中例如在某种表达式中访问ref_to_c值,那么它将是UB,因为该引用实际上是对未初始化的内存进行别名,这将被初始化在member2构造期间,这将在以后发生。

同样的问题将出现在B析构函数中,其中member2将在ref_to_c之前销毁。

如果在A中交换member2和member1会更好,这样您就可以使用构造对象初始化引用,从而定义每个可能的用法。