常见错误45: dynamic_cast带来的多义性解析失败----读书笔记《c++ gotchas》...
时间:2010-08-13 来源:lzueclipse
问题:决定一个抽象的Screen对象是否为EntryScreen(继承自Screen),还是其他继承自Screen的类型。
你的第一个冲动可能是扩张所有Screen:
class Screen {
public:
//…
virtual bool isEntryScreen() const {
return flase;
}
};
class EntryScreen : public Screen {
public:
bool isEntryScreen() const {
return true;
}
};
//…
Screen *getCurrent();
//…
if(getCurrent()->isEntryScreen())
//…
这种做法的问题在于赋予了查询Screen对象的动态类型一事以合法地位。
这就捅了马蜂窝,未来会有更多的此类问题出来:
class Screen {
public;
//…
virtual bool isEntryScreen() const {return false;}
virtual bool isPricingScreen() const {return false;}
virtual bol isSwapScreen() const {return false;}
//没玩没了
};
显然,这么一个接口设计出来就是为了被下面这样用的:
//…
if(getCurrent()->isEntryScreen())
//…
else if(getCurrent()->isPricingScreen())
//…
else if(getCurrent()->isSwapScreen())
//…
这时应该考虑dynamic_cast。
if(EntryScreen *es = dynamic_cast<EntryScreen *>(sp) ) {
//做有关EntryScreen的操作
}
如果强制类型转换成功,es或是指向一个完整的EntryScreen对象,或是继承EntryScreen的对象中的EntryScreen子对象。
dynamic_cast进行强制类型转换可能由于以下四种原因的任何一种而失败,返回一个NULL pointer:
1)强制类型转换无法进行
如果sp根本不是
指向一个完整EntryScreen对象,
或指向继承自EntryScreen的某个继承类型对象的子对象的话,会失败
2)sp本身是空指针
3)把某个根本无法访问的基类作为强制类型转换的源类型或目的类型
4)强制类型转换多义性
类型转换的多义性在设计良好的继承谱系中不常见,但在结构和访问层级设计很差的继承谱系中却时时现身:
这是个多继承的继承谱系,假定A类型有虚函数,并且此处只是使用了public方式的继承,那么一个D对象含有
2个A类型的子对象。
D *dp = new D;
A *ap = dp;//多义性错误
ap = dynamic_cast<A*> dp;//多义性错误
B *bp = dynamic_cast<B *>dp;//正确
C *cp = dynamic_cast<C *>dp;//正确
如果两次A类型被用作基类时皆以虚继承方式进行,多义性问题也就不存在了。
把继承谱系弄得更复杂点,以重新引入多义性:
A *ap = new D;//无多义性
E *ep = dynamic_cast<E *> (ap);//多义性
只要再精确些:
E *ep = dynamic_cast<B *>(ap);//没问题
注:A*类型转换到B*类型,B*类型再转换到E*类型。最好还是用虚继承简化继承谱系。
讨论下dynamic_cast的精妙之处:
dynamic_cast不一定是动态的,因为它不一定做运行期校验。
使用dynamic_cast把一个派生类类型的指针(或引用)转换到它的以public方式继承的基类(此处说的是向上转型,
而不是向下转型),不用做运行期校验。
这种情况下,强制类型转换时不需要的。
如果把一个派生类类型的指针转换至void *指针,也不做运行期检查。可是仅仅得到了指针,有啥用处!?