天天看点

dynamic_cast的使用条件

今天从学长那里听来一道腾讯的面试题,问的是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。

继续阅读