C++多继承动态交叉转换dynamic_cast

来源:互联网 发布:网络鲜花速递 编辑:程序博客网 时间:2024/05/05 02:52

1. 问题简述:

         有如下源代码:
         
Class BaseA;Class BaseB;Class SubClass:public BaseA,public BaseB{...}
      现有如下变量定义:
 
BaseA* insA = new SubClass(...);BaseB* insB;
请问如何将insA指向的对象正确赋值给BaseB*的指针insB? 有下面两种方法:
  1. 既然我们知道insA是SubClass类型,可以先把父类的指针insA转换成子类的指针类型SubClass*,然后在上行转换成父类型BaseB:insB = (BaseB*)( ( SubClass* )insA)
  2. 调用C++的dynamic_cast函数,它会先做类型检查,以保证类型正确,如果不能转换,会出错:insB =  dynamic_cast<BaseB*>(insA)

2. 例子剖析:

源代码:

#include <iostream>using namespace std;// class BaseA;// class BaseB;// class SubClass;class BaseA{private:int m_baseA;int m_moreSpace;public:BaseA(int a = 0){m_baseA = a;//lala = 34;m_moreSpace = 1111111;}virtual int getA(){return m_baseA;}virtual int getMoreSpace(){return m_moreSpace;}};class BaseB{private:int m_baseB;public:BaseB(int b = 0){m_baseB = b;}virtual int getB(){return m_baseB;}virtual int forA(){cout<<"\t+++++++++++++++++++++++++++++++++++++++++++++++++++"<<endl;cout<<"\tyou call me ,haha!"<<endl;cout<<"\t+++++++++++++++++++++++++++++++++++++++++++++++++++"<<endl;return 999999999;}};//先给BaseA开辟空间,然后给BaseB开辟数据空间,最后给自定义类开辟数据空间class SubClass:public BaseA,public BaseB{private:int m_subInt;public:SubClass(int sub = 0,int basea=0,int baseb=0):BaseB(baseb),BaseA(basea){m_subInt = sub;}int getSubInt(){// cout<<"SubClass:BaseA:getA()"<<getA()<<endl;// cout<<"SubClass:BaseB:getB()"<<getB()<<endl;return m_subInt;}};int main(){SubClass* ins = new SubClass(11,22,33);//a=22,b=33;cout<<"((SubClass*)ins) -> getSubInt():"<<((SubClass*)ins) -> getSubInt()<<endl;cout<<"c cast:\n\t(BaseA*)ins->getA():"<<((BaseA*)ins)->getA()<<endl; //22cout<<"c cast:\n\t(BaseB*)ins->getB():"<<((BaseB*)ins)->getB()<<endl; //33BaseA* insA = ins;cout<<"c cast:\n\t(BaseA*)insA->getA():"<<((BaseA*)insA)->getA()<<endl; //22cout<<"c cast:\n\t(BaseB*)insA->getB():"<<((BaseB*)insA)->getB()<<endl; //22BaseB* insB = ins;cout<<"c cast:\n\t(BaseA*)insB->getA():"<<((BaseA*)insB)->getA()<<endl; //33/*****************************************************************************************//*  如果A中的函数getMoreSpace为虚函数,那么此处会报错,因为insB中并没有第二个虚函数,如果B中有第二个虚函数的话, 就会调用B中的虚函数/*****************************************************************************************/cout<<"c cast:\n\t(BaseA*)insB->getMoreSpace():"<<((BaseA*)insB)->getMoreSpace()<<endl; //11,sub值,访问越界cout<<"c cast:\n\t(BaseB*)insB->getB():"<<((BaseB*)insB)->getB()<<endl; //33cout<<"using dynamic_cast.............................................."<<endl;/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////*在使用dynamic_cast的时候,类型中一定要有虚函数,否则会编译报错*///////////////////////////////////////////////////////////////////////////cout<<"c++ cast:\n\t( dynamic_cast<BaseA*>(insA) )->getA():"<< ( dynamic_cast<BaseA*>(insA) ) -> getA()<<endl; //22cout<<"c++ cast:\n\t( dynamic_cast<BaseB*>(insA) )->getB():"<<( dynamic_cast<BaseB*>(insA) ) -> getB()<<endl; //33cout<<"c++ cast:\n\t( dynamic_cast<BaseA*>(insB) )->getA():"<< ( dynamic_cast<BaseA*>(insB) )->getA()<<endl; //22cout<<"c++ cast:\n\t( dynamic_cast<BaseB*>(insB) )->getB():"<<( dynamic_cast<BaseB*>(insB) )->getB()<<endl; //33cout<<"another choice:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::"<<endl;cout<<"c cast:\n\t((BaseA*)( (SubClass*)insA ))->getA():"<< ((BaseA*)( (SubClass*)insA )) -> getA()<<endl; //22cout<<"c cast:\n\t((BaseB*)( (SubClass*)insA ))->getB():"<< ((BaseB*)( (SubClass*)insA )) -> getB()<<endl; //33cout<<"c cast:\n\t((BaseA*)( (SubClass*)insB ))->getA():"<< ((BaseA*)( (SubClass*)insB ))->getA()<<endl; //22cout<<"c cast:\n\t((BaseB*)( (SubClass*)insB ))->getB():"<< ((BaseB*)( (SubClass*)insB ))->getB()<<endl; //33return 0;};

运行结果:


逐行剖析:

cout<<"c cast:\n\t(BaseA*)ins->getA():"<<((BaseA*)ins)->getA()<<endl; //22cout<<"c cast:\n\t(BaseB*)ins->getB():"<<((BaseB*)ins)->getB()<<endl; //33
由于ins是子类SubClass类型的指针,进行上行转换,C++会自动计算偏移量,假设ins的地址为0x00000000,那么(BaseA*)ins的地址也会是0x00000000,(BaseB*)ins的地址会是0x00000000+3*4(说明:BaseA两个数据成员加上一个虚函数表指针)

BaseA* insA = ins;cout<<"c cast:\n\t(BaseA*)insA->getA():"<<((BaseA*)insA)->getA()<<endl; //22cout<<"c cast:\n\t(BaseB*)insA->getB():"<<((BaseB*)insA)->getB()<<endl; //22

将ins赋值给BaseA类型的指针后,它就失去了作为BaseB之类的性质,在进行转换(BaseB*)insA的时候,它并不能计算偏移值,也就是说,(Base*)insA后的地址和没有转换的一样。然后再调用方法getB(),按照常规,它应该调用类型BaseB的方法,可是由于当前指向地址为BaseA*的起始地址,调用getB其实就是调用虚函数表中的第一个方法,而当前指向地址中虚函数表的第一个方法为BaseA的getA(),所以,两次调用得到结果都是22。具体地址参照截图:




BaseB* insB = ins;cout<<"c cast:\n\t(BaseA*)insB->getA():"<<((BaseA*)insB)->getA()<<endl; //33cout<<"c cast:\n\t(BaseB*)insB->getB():"<<((BaseB*)insB)->getB()<<endl; //33
这里和刚才上面分析的一样,就不再赘述:直接看图:




cout<<"c cast:\n\t(BaseA*)insB->getMoreSpace():"<<((BaseA*)insB)->getMoreSpace()<<endl; 

这里和上面讲述的道理一样,不过getMoreSpace()是第二个虚函数,所有会调用虚函数表中的第二项,而insB是BaseB指针类型,所有会调用BaseB中的第二个虚函数forA(),(说明:如果BaseB并没有第二虚函数,会导致运行时错误)见图:

使用动态转换dynamic_cast和先转子类再转父类的代码就不在详述了,注意:在使用dynamic_cast的时候,必须保证目标类是多态类,多态类后文有说明。


多态类:

参考:http://www.cnblogs.com/weidagang2046/archive/2010/04/10/1709226.html
多态类型在使用时需要注意:被转换对象obj的类型T1必须是多态类型,即T1必须公有继承自其它类,或者T1拥有虚函数(继承或自定义)。若T1为非多态类型,使用dynamic_cast会报编译错误。下面的例子说明了哪些类属于多态类型,哪些类不是://A为非多态类型 class A{};//B为多态类型class B{     public: virtual ~B(){}};//D为多态类型class D: public A{};//E为非多态类型class E : private A{};//F为多态类型class F : private B{}
参考:http://www.cnblogs.com/weidagang2046/archive/2010/04/10/1709226.html

总结:

         在进行交叉转换的时候,尽量使用C++类型安全的dynamic_cast。不过建议尽量少使用多继承,能单继承的就尽量简单化。
         小弟不才,诸多错误,还望各位大神多多指教。