假设我有以下内容:
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是否存在于初始化列表阶段,还是未定义的行为?
答案 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会更好,这样您就可以使用构造对象初始化引用,从而定义每个可能的用法。