在对象构造期间,将指向尚未构造的子对象的指针传递给另一个子对象的构造函数是否危险?



子对象在构造之前不会被使用,只会存储指向它的指针。

请考虑以下代码:

class Base1;
class Base0 
{
public:
Base0(Base1* obj1) : 
anotherObj(new AnotherClass(obj1)) // only saves the pointer, doesn't use the object 
{}
AnotherClass* anotherObj;
};
class Base1 { /*...*/ };
class Derived : public Base0, public Base1
{
public:
Derived() :
Base0(this), // expecting an implicit conversion of Derived* to Base1*
Base1()
{}
};

是否可以在进行任何碱基构造之前将此指针向上投射到任何碱基并获得有效的指针?那么虚拟继承等更复杂的情况呢?

一般来说,这是一种有点危险的模式。可以保持严格有效,但在所示的示例中,实际上是标准不允许从Base1*AnotherClass*的转换。

[class.cdtor]/2 对将指针从派生类转换为基类提供了一些限制:

显式或隐式地将引用类X对象的指针(glvalue(转换为指向X的直接或间接基类B的指针(引用(,X的构造及其直接或间接派生自B的所有直接或间接基础的构造应该已经开始,并且这些类的销毁尚未完成, 否则,转换会导致未定义的行为。

当计算Base0子对象的初始值设定项"this"时,Derived的构造已经开始,但其Base1子对象的构造尚未开始。但是由于继承树中没有其他类派生自Base1Derived*Base1*转换是可以的。如果父类构造函数尚未启动,则this转换为指向祖父类的指针是不行的;如果祖父类是虚拟基类,则在继承它的最后一个中间类开始构造之前不允许转换!

首先,转换后的指针指向一个对象,其(非平凡的(构造尚未开始,因此它受到 [class.cdtor]/1 和 [basic.life]/6 中的限制。大多数情况下,您还不能获取其任何基类子对象或成员。复制该指针,而不进行任何进一步的隐式基类指针转换,几乎是您可以合法地使用它执行的所有操作。因此,该示例从Base1*AnotherClass*的隐式转换会导致未定义的行为。不过,如果anotherObj成员具有完全相同的类型Base1*也没关系。

最新更新