6

这个问题和这个问题非常相似Why can't I dynamic_cast "sideways" during multiple继承?,除了演员表确实有效 - 只是不在构造函数内部。

标题:

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 之前被调用。

我显然不明白幕后到底发生了什么,有人可以赐教吗?

谢谢,凸轮班伯。

4

5 回答 5

7

基本上,标准说它在构建对象期间不起作用(dynamic_cast)。<报价>

编辑:根据下面的 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 指针(用户定义的代码)。因此,使用thisin B::B() 作为指向 C 对象的指针失败,因此当在此调用 dynamic_cast<> 时,由于 UB,它无法执行您想要的操作。

12.7 构造和销毁 [class.cdtor]

第 3 段

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

[ 例子:

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
      } 
};

—结束示例]

</quote>

于 2011-06-09T21:21:57.737 回答
3

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

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

于 2011-06-09T21:18:59.233 回答
2

然后在构造过程A中动态类型是A不管的。这是因为您将在构造之前开始调用派生类的成员函数并访问派生成员变量,这将是 UB 并且非常糟糕。

于 2011-06-09T21:08:12.990 回答
2

由于B不继承自A(B是最父类),因此B其构造函数期间的动态类型是B. 只有当 theABparents 都被构造时,childC才能被构造,允许侧身dynamic_casting。

于 2011-06-09T21:08:22.607 回答
0

它在 B 内部不起作用,因为 B 不是从 A 继承的

于 2011-06-09T21:09:39.347 回答