这个问题与这个问题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。
答案 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而无法按照你的意愿行事。
第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_cast
和typeid
所需的信息。在每个构造函数之前,更新它以指向当前正在构造的类的信息。
答案 2 :(得分:2)
在构建A
期间,动态类型为A
,无论如何。这是因为您将在构造派生类之前调用派生类的成员函数并访问派生成员变量,这些变量将是UB并且非常糟糕。
答案 3 :(得分:2)
由于B
未从A
继承(B
是最多父类),因此B
在其构造函数中的动态类型为B
。只有在构建A
和B
父项时,才能构建子C
,从而允许横向dynamic_cast
。
答案 4 :(得分:0)
它在B内部不起作用,因为B不从A
继承