2016-12-24 25 views
10

比方说,我有以下几点:在初始化列表中引用类成员是否是未定义的行为?

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

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

class C { 
... 
}; 

B要求,要下的参考它的构造函数中提供。如果类A提供C,是合法的,指定的初始化器列表,下面...

A() : member1(B(member2)) {} 

也就是说,不member2在初始化器列表相存在,或者是这个不确定的行为

+1

member1在成员2之前初始化..我不确定UB,但我认为会发生错误 –

+0

顺便说一下,GCC 4.9.2根本没有抱怨 –

+0

@Galik'member2'不是临时的,临时的'B'初始化为'member1',并调用复制构造函数或移动构造函数,这里没有错。 –

回答

14

Intialization如下:

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

- 第一,并且仅用于下面描述的最派生类如 的构造,虚基类应按照 的顺序进行初始化,它们出现在 基本类的有向无环图的深度优先从左到右的遍历中,其中“从左到右”是基类名中出现的 的顺序。派生类 基地说明符列表。

- 然后,直接基类应以 的声明顺序进行初始化,因为它们出现在基指定符列表中(不管 这些mem初始化符的顺序如何)。

- 然后,非静态数据成员应在 的顺序,他们在类定义中声明(再次不管 顺序MEM-初始化的)初始化。

- 最后,构造函数的主体被执行。 [注意: 声明订单的任务是确保基本和成员 子对象以与初始化相反的顺序销毁。 ]


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

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

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

C member2; 
B member1; 
4

你弥补member1包含对memebr2参考。 这还没有构建,但编译器已经知道它会在哪里(所以它可以提供一个参考)。

它将工作,但将UB如果你尝试 - 例如B中ctor-某种表达的访问ref_to_c值,由于基准实际上是混淆了未初始化的内存,这将member2施工期间进行初始化,那会在稍后发生。

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

如果您在A中交换member2和member1,那么您将使用构造对象初始化引用,从而定义每种可能的用法。

相关问题