C++学习之显示类型转换与运行时类型识别RTTI(二)
//处理类型转换失败的情况
}
}
void f2(){//指针类型的dynamic_cast
A *ap = new A();//使用基类对象的【指针】
if(B *bp = dynamic_cast(ap)){//动态转换ap为指向B类型的指针;成功则继续,不成功则为0
bp->fun4(); //执行某个‘派生类操作’并且’该操作‘不是‘虚函数’
//其他操作
}else{
//使用ap指向的A对象
}
delete ap; ap=NULL;
}
如果对无继承关系或者没有虚函数的对象指针进行转换、基本类型指针转换以及基类指针转换为派生类指针,都不能通过编译。
哪种情况下dynamic_cast和static_cast使用的情况一样?
【代码6】
class C{
//类内定义
};
class D:public A, public C{ //多重继承
//类内定义
};
void f3(){ //单继承情况下
//如果ap指向的即为B的对象,则使用dynamic_cast和static_cast效果一样
A *ap = new B();
B *bp = dynamic_cast(ap); //A要有虚函数,否则使用dynamic_cast会产生编译错误,
B *bp1 = static_cast(ap); //static_cast则没有这个限制
delete ap; delete bp; delete bp1;
ap=bp=bp1=NULL; //防止野指针
//如果ap指向的不为B的对象,则用dynamic_cast返回NULL,能够更早的禁止error的发生;
//如果用static_cast返回的不为NULL,但是运行时可能抛出异常
A *ap = new A();
B *bp = dynamic_cast(ap); //正确,但bp指向的为NULL
B *bp1 = static_cast(ap); //错误,bp1在运行时可能会抛出异常
delete ap; delete bp; delete bp1;
ap=bp=bp1=NULL; //防止野指针
}
void f4(){ //多继承情况下
//如果ap指向的即为C的对象,则使用dynamic_cast和static_cast效果一样,都可以转换为C的指针
A *ap = new B();
C *cp = dynamic_cast(ap);
C *cp1 = static_cast(ap);
//若要将ap转换为dp,则都可以,只是dynamic_cast可以一次转换,static_cast需要一个过渡;dp的转换相当于cp1和dp1,相当于dp2
D *dp = dynamic_cast(ap);
D *dp1 = static_cast(cp1);
D *dp2 = static_cast(static_cast(ap));
delete ap; delete cp; delete cp1; delete dp; delete dp1; delete dp2;
ap=cp=cp1=dp=dp1=dp2=NULL; //防止野指针
//如果ap指向的不为C的对象,则用dynamic_cast返回NULL,能够更早的禁止error的发生;情况与f3中一样!
}
dynamic_cast可用于进行交叉转换:
【代码7】
//类内定义
};
void f5(){ //交叉转换
//使用static_cast进行转换是不被允许的,将在编译时出错;而使用 dynamic_cast的转换则是允许的,结果是空指针。
B *bp = new B();
E *ep = dynamic_cast(bp);//正确,返回空指针
E *ep1 = static_cast(bp);//错误,编译时出错
//其他内容
}
dynamic_cast的讨论:
在探究 dynamic_cast 的设计意图之前,值得留意的是很多 dynamic_cast 的实现都相当慢。 例如,至少有一种通用的实现部分地基于对类名字进行字符串比较。如果你在一个位于四层深的单继承体系中的对象上执行 dynamic_cast,在这样一个实现下的每一个 dynamic_cast 都要付出相当于四次调用 strcmp 来比较类名字的成本。对于一个更深的或使用了多继承的继承体系,付出的代价会更加昂贵。
对 dynamic_cast 的需要通常发生在这种情况下:你要在一个你确信为派生类的对象上执行派生类的操作,但是你只能通过一个基类的指针或引用来操控这个对象。 有两个一般的方法可以避免这个问题:
使用存储着直接指向派生类对象的指针的容器,从而消除通过基类接口操控这个对象的需要。当然,这个方法不允许你在同一个容器中存储所有可能的基类的派生类的指针。为了与不同的窗口类型一起工作,你可能需要多个类型安全(type-safe)的容器。
通过一个基类的接口操控所有可能的 Window 派生类,就是在基类中提供一个让你做你想做的事情的虚函数。例如,尽管只有 SpecialWindows 能 blink,在基类中声明这个函数,并提供一个什么都不做的缺省实现或许是有意义的。
所以:避免强制转型的随意应用,特别是在性能敏感的代码中应用 dynamic_casts,如果一个设计需要强制转型,设法开发一个没有强制转型的侯选方案。 如果必须要强制转型,设法将它隐藏在一个函数中。客户可以用调用那个函数来代替在他们自己的代码中加入强制转型。
什么情况下使用dynamic_cast代替虚函数?
当基类代码不可知,需要在派生类里面新增新成员函数,但是又无法取得基类的源代码,不能在基类里面通过加虚函数接口调用新成员函数,可以通过dynamic_cast强制转换来获得调用。如:
【代码8】
class A{
public:
virtual void fun(){
cout<<"A::fun"<
}
//其他代码未知,不能更改A的代码
};
class B:public A{
public:
void fun(){