今天从学长那里听来一道腾讯的面试题,问的是C++中的dynamic_cast在什么情况下是错误的?我认为这个问题的更好的描述是dynamic_cast的使用条件是什么?
C++提供了两种方式来支持RTTI,dynamic_cast是其中一种,另一种是typeid()。表面上看,dynamic_cast有两种形式:
Base *pBase = new Derived();
Derived *pDerived = dynamic_cast<Derived *> pBase;
Base refBase = Derived();
Derived &refDerived = dynamic_cast<Derived &> refBase
即dynamic_cast用于将基类的引用或指针转化为派生类的引用或指针。但使用条件是:1、基类的指针或引用确实绑定到派生类的对象上;2、只有当基类至少含有一个虚函数的时候才能使用dynamic_cast。原因是RTTI机制依赖于虚函数表(inside C++ object model第一章给出了解释),而dynamic_cast是RTTI的一种,所以必须要有虚函数表的支持,也就是要虚类中至少有一个虚函数。
那么,dynamic_cast有什么好处呢?
我们知道,基类的指针即使指向的是派生类的对象,但通过基类的指针也只能访问到基类中包含的public成员,那如果想要访问派生类中新增的public成员呢?这就需要将基类的的指针动态类型转化为派生类的指针。见以下代码:
#include<iostream>
using namespace std;
class base{
public:
base(int x):a(x){ }
virtual void fcn(){ } //要有虚函数
int a;
};
class derived:public base{
public:
derived(int x,int y):base(x),b(y){ }
int b;
};
int main(){
base *pbase = new derived(100, 200);
cout << pbase->a << endl;
//cout << pbase->b << endl; //不能通过基类的指针访问派生类新增的public成员
derived *pderived = dynamic_cast<derived *>(pbase);
cout << pderived->a << endl;
cout << pderived->b << endl;
}
如何对dynamic_cast转化是否成功进行判断,从而选择进行基类的操作还是进行派生类的操作呢?见下面的代码:
#include<iostream>
using namespace std;
class base{
public:
base(int x):a(x){ }
virtual void fcn(){ } //要有虚函数
int a;
};
class derived:public base{
public:
derived(int x,int y):base(x),b(y){ }
int b;
};
int main(){
base *pbase0 = new base(100);
base *pbase1 = new derived(100, 200);
if(NULL != dynamic_cast<derived *>(pbase0)){
cout << dynamic_cast<derived *>(pbase0)->b << endl;
cout<<"derived"<<endl;
}else{
cout << pbase0->a << endl;
cout<<"base"<<endl;
}
if(derived *pderived = dynamic_cast<derived *>(pbase1)){
cout << dynamic_cast<derived *>(pbase1)->b << endl;
cout<<"derived"<<endl;
}else{
cout << pbase1->a << endl;
cout<<"base"<<endl;
}
}
运行结果如下:
100
base
200
derived
《C++ primer》第5版P731上给出了以下代码框架:
if(Derived *dp = dynamic_cast<derived *>(bp)){
//使用bp指向的Derived对象
}else{
//使用bp指向的Base对象
}
在vs和gcc下测试过:如果bp指向的不是派生类的对象而是基类的对象的话,那么两种编译器下都不能通过编译!!!这样的运行时动态类型判断也就没有意义了。这种写法值得商榷!
如果是引用的话,当转化不成功时会抛出std::bad_cast异常,详见《C++ primer》第5版P731。