要使用dynamic_casting,继承层次结构需要是多态的。但是在下面的代码段中,dynamic_cast完美地适用于向上转换,而在编译时它为了向下转换失败了吗?升级的原因是什么?
class B{};
class C : public B{};
int main()
{
C* pcob = new C();
B* bp = dynamic_cast<B*>(pcob); // #1 : Upcasting works
if(bp)
cout << "casted" << endl;
else
cout << "bp null" << endl;
delete bp;
B* pbob = new B();
C* pc = dynamic_cast<C*>(pbob); // #2 : Downcasting gives error
if(pc)
cout << "casted" << endl;
else
cout << "pc null" << endl;
delete pbob;
return 0;
}
#2的编译时错误是
main.cpp: In function ‘int main()’:
main.cpp:36:34: error: cannot dynamic_cast ‘pbob’ (of type ‘class B*’) to type ‘class C*’ (source type is not polymorphic)
C* pc = dynamic_cast<C*>(pbob);
答案 0 :(得分:3)
类B
至少需要一种virtual
方法。否则,该类型不是多态的,dynamic_cast
不能应用于向下转换。前一种情况是隐式类型转换。
但是明确使用了dynamic_cast。编译器如何忽略它?
来自标准(5.2.7 / 5)
如果T是“指向cv1 B的指针”并且v具有类型“指向cv2 D的指针”,使得B是D的基类,则结果是 指向由v指向的D对象的唯一B子对象的指针。类似地,如果T是“对cv1 B的引用”并且 v具有类型cv2 D,使得B是D的基类,结果是引用的D对象的唯一B子对象 通过v。 68 如果T是左值引用,则结果为左值;如果T是右值引用,则结果为x值。在 如果cv2具有比cv1更大的cv资格,或者如果B是D的不可访问或模糊的基类,那么程序的指针和引用情况都是错误的。[例如:
struct B { };
struct D : B { };
void foo(D* dp) {
B* bp = dynamic_cast<B*>(dp); // equivalent to B* bp = dp;
}
-end example]
答案 1 :(得分:1)
真正设定理论
答案 2 :(得分:1)
dynamic_cast
不需要多态类型进行向上转换。
5.2.7 / 1 [expr.dynamic.cast]
中的一些基础知识表达式dynamic_cast(v)的结果是将表达式v转换为类型T的结果。
然而,这并未涵盖为什么类型不必是多态的。为了更好地理解5.2.7 / 5和5.2.7 / 6中的以下引用,请稍微清楚一点。
如果T是“指向cv1 B的指针”并且v具有类型“指向cv2 D的指针”,使得B是D的基类,则结果是指向指向D对象的唯一B子对象的指针通过v。
struct B { };
struct D : B { };
void foo(D* dp) {
B* bp = dynamic_cast<B*>(dp); // equivalent to B* bp = dp;
}
上面提供了使用兼容的 cv 限定符(const-volatile)从D到B向上转换的特定实例。
下一部分5.2.7 / 6更清楚地说明为什么源类型不必是多态的
否则,v应该是多态类型的指针或左值。
这里的关键是使用“否则”。从另一个方向看这个如果D不能隐式转换为B,那么D必须是多态的否则不需要多态类型。
答案 3 :(得分:1)
标准被引用 - 赞美。
为了理解标准为什么指定它所做的行为,有助于考虑你要求编译器做什么以及它与运行时类型信息的常见实现有什么关系....
您的dynamic_cast<B*>(pcob)
只需要知道B
相对于包含C
的偏移量 - 这是每个C
对象的常量,即使C
dynamic_cast<C*>(pbob)
1}}嵌入在其他对象中。直到运行时才会依赖任何未知的东西。
将B* pbob
与B
进行对比 - 编译器通常无法知道指向的C
是否嵌入到另一个对象中,更不用说该对象是否恰好发生在是 - 或者也可以从 - B*
派生出来,这样演员才能成功。通常,它需要依赖仅为多态类型生成的运行时类型信息。
在您的特定代码中,编译器知道所有对象的实际类型,并且只需要很少的努力就可以支持向下转换,但是在获得B*
参数或调用某些不透明的函数中的类似代码返回dynamic_cast
的工厂方法有时可能C
包含{{1}}的运行时对象,有时则不包括 - 编译器不能使用任何实际类型的“本地化”知识。在本地使用中,您可以轻松地创建指向实际运行时类型的指针,因此要求编译器支持“本地化”使用不会获得任何实用程序,而标准只是批准禁止批量转换。