练习19.3
已知存在如下的类继承体系,其中每个类分别定义了一个公有的默认构造函数和一个虚析构函数:
class A{/* ... */};
class B : public A{/*...*/};
class C : public B{/*...*/};
class D : public B, public A{/*...*/};
下面的哪个dynamic_cast将失败?
(a) A *pa = new C;
B *pb = dynamic_cast< B* >(pa);
(b) B *pb = new B;
C *pc = dynamic_cast< C* >(pb);
(c) A *pa = new D;
B *pb = dynamic_cast< B* >(pa);
解答:
感谢asjdhs同学的提醒(详见评论),之前的代码的确有一些问题。
这里需要注意的是”注意转换指针的时候不会抛异常,只有在转引用的时候才会抛异常“。
更新如下:
测试代码如下:
#include <iostream>
using namespace std;
class A{
public:
A() = default;
virtual ~A(){}
};
class B : public A{
public:
B() = default;
virtual ~B(){}
};
class C : public B{
public:
C() = default;
virtual ~C(){}
};
class D : public B, public A{
public:
D() = default;
virtual ~D(){}
};
#define choose 1
int main(){
#if choose == 1
A *pa = new C;
B *pb = dynamic_cast<B*>(pa);
cout << "a's result: pb's address is " << static_cast<void*>(pb) << endl;
#elif choose == 2
B *pb = new B;
C *pc = dynamic_cast<C*>(pb);
cout << "b's result: pc's address is " << static_cast<void*>(pc) << endl;
#else
A *pa = new D;
B *pb = dynamic_cast<B*>(pa);
cout << "c's result: pb's address is " << static_cast<void*>(pb) << endl;
#endif
}
这里我们将编译器换为clang3.4。
这里使用宏choose的原因是为了分开不同的小题(因为不同的题会在runtime和compiling-time有问题)
a) 当choose为1时,转换是没有问题的,你会在终端或命令行窗口中看到指针指向的地址。
b) 当choose为2时,转换就会出问题了,这个时候你会在命令行或终端窗口中看到指针指向的地址为0。这是C++在运行时对类型的检查。
这里原因可以参见MSDN(https://msdn.microsoft.com/en-us/library/cby9kycs.aspx)的 dynamic_cast_3.cpp上的解释。
c) 当choose为其他值时,编译都会出问题。我们来看看clang和gcc给出的错误信息:
clang3.4
test.cc:42:11: error: ambiguous conversion from derived class 'D' to base class 'A':
class D -> class B -> class A
class D -> class A
A *pa = new D;
^~~~~
1 error generated.
gcc4.9.2
test.cc:42:15: error: ‘A’ is an ambiguous base of ‘D’
A *pa = new D;
^
这里gcc的提示很简练,clang的提示很丰富。要表达的意思都是一个,因为这里使用到了多重继承,所以这里转换有二义性。
这里有疑问的是从那种继承方式往A进行转换(如同clang给出的提示那样)。
MSDN(上面网址)的 dynamic_cast_5.cpp上的解释,大家可以参考。
练习19.4
使用上一个练习定义的类改写下面的代码,将表达式*pa转换成类型C&:
if(C *pc = dynamic_cast< C* >(pa)){
// 使用C的成员
} else {
// 使用A的成员
}
解答:
我的理解如下:
int main(){
A *pa = new C;
try{
C &pc = dynamic_cast<C&>(*pa);
}
catch (bad_cast &e){
cout << e.what() << endl;
}
}
练习19.5
在什么情况下你应该使用dynamic_cast替代虚函数?
解答:
【引用】我们想使用基类对象指针或引用执行某个派生类操作并且该操作不是虚函数。