内部构造函数中的“this”的dynamic_cast

时间:2011-06-09 21:04:55

标签: c++ constructor this multiple-inheritance dynamic-cast

这个问题与这个问题Why can't I dynamic_cast "sideways" during multiple inheritence?非常相似,只是演员阵容确实有效 - 只是不在构造函数内部。

标题:

class A  
{  
public:  
    virtual                ~A() {}
    void                    printA();
};

class B
{
public:
                            B();
    virtual                ~B() {}
    void                    printB();

private:
    std::string             message_;
};

class C : public A, public B
{
public:
                        C() {}
    virtual                ~C() {}
};

来源:

void A::printA() { cout << "A" << endl; }
B::B()
{
    A* a = dynamic_cast< A* >( this );
    if ( a ) {
        message_ = std::string( "A and B" );
    } else {
        message_ = std::string( "B" );
    }
}
void B::printB() { cout << message_.c_str() << endl; }

主:

int main( int argc, char* argv[] )
{
    cout << "Printing C..." << endl;
    C c;
    c.printA();
    c.printB();

    cout << "Checking again..." << endl;
    cout << !!dynamic_cast< A* >( &c ) << endl;

    return EXIT_SUCCESS;
}

结果:

Printing C...
A
B
Checking again...
1

因此,dynamic_cast确实适用于多重继承(没有意外!),但为什么不在运行时调用B :: B()中的'this'指针?我认为对象在构造函数体内完全形成,即所有内存都是为组件对象分配的,它们尚未初始化。我理解这取决于超类构造函数的顺序,但是在这个例子中,A在B之前被调用。

我显然不明白究竟发生了什么,有人可以赐教我吗?

谢谢, Cam Bamber。

5 个答案:

答案 0 :(得分:7)

基本上标准表示在构造对象期间它不起作用(dynamic_cast)。 &LT;报价&GT;

编辑:根据下面的VJo评论添加。

注意:使用动态强制转换从'B'到'A'的转换应该有效,因为我们正在转换类型为'C'的对象。如果我们将以下代码添加到main:

B  bObj;
B& bRef = c;
B* bPtr = &c;
std::cout << !!dynamic_cast<A*>(&bObj) << std::endl;
std::cout << !!dynamic_cast<A*>(&bRef) << std::endl;
std::cout << !!dynamic_cast<A*>( bPtr) << std::endl;

额外的输出是:

0   // Can not convert a B to an A
1   // Can convert this B to an A because it is really a C.
1   // This is  what we are reeling doing in B::B() that fails
    // It is not the dynamic_cast<> that fails but the conversion of this from C* to B*
    // That is causing UB

它在构造函数中失败,因为该对象未完全形成。使用这个,我们试图在C构造函数启动之前将C指针转换为B指针(用户定义的代码)。因此,当动态广播&lt;&gt;时,在B :: B()中使用this作为指向C对象的指针失败。因此而被调用因为UB而无法按照你的意愿行事。

12.7建设和销毁[class.cdtor]

第3段

  

显式或隐式地将引用类X的对象的指针(glvalue)转换为指向X的直接或间接基类B的指针(引用),构造X及其所有直接构造或者直接或间接从B派生的间接基础应该已经开始,并且这些类别的销毁不应该完成,否则转换会导致不确定的行为。要形成指向对象obj的直接非静态成员(或访问其值)的指针,obj的构造应该已经开始并且它的销毁不应该完成,否则计算指针值(或访问成员)值)导致未定义的行为。

[例如:

struct A { };
struct B : virtual A { };
struct C : B { };
struct D : virtual A { D(A*); };
struct X { X(A*); };
struct E : C, D, X 
{ 
    E() : D(this),  // undefined: upcast from E* to A*
                    // might use path E* → D* → A* 
                    // but D is not constructed 
                    // D((C*)this), 
                    // defined: 
                    // E* → C* defined because E() has started 
                    // and C* → A* defined because
                    // C fully constructed 
      X(this) { // defined: upon construction of X,
                    // C/B/D/A sublattice is fully constructed
      } 
};

- 结束示例]

&LT; /报价&GT;

答案 1 :(得分:3)

每个基类构造函数在派生类构造函数之前执行,并且在B构造函数中,对象的动态类型为B;在您输入C构造函数之前,它不会变为C。所以你不能做任何需要动态类型C的事情:你不能交叉转换到任何C的其他基类,如果你调用了一个虚函数,那你就不会得到C提供的任何覆盖。

在幕后,动态类型(在大多数实现中至少)由对象中的指针(称为“vptr”)确定,该指针指向指定类的属性的一些静态数据,包括表格虚拟函数(称为“vtable”)以及dynamic_casttypeid所需的信息。在每个构造函数之前,更新它以指向当前正在构造的类的信息。

答案 2 :(得分:2)

在构建A期间,动态类型为A,无论如何。这是因为您将在构造派生类之前调用​​派生类的成员函数并访问派生成员变量,这些变量将是UB并且非常糟糕。

答案 3 :(得分:2)

由于B未从A继承(B是最多父类),因此B在其构造函数中的动态类型为B 。只有在构建AB父项时,才能构建子C,从而允许横向dynamic_cast

答案 4 :(得分:0)

它在B内部不起作用,因为B不从A

继承