dynamic_cast在什么时候使用:
使用时间:子类和父类之间的多态类型转换
引入dynamic_cast的原因:
举例说明:
#include <iostream> using namespace std; class Base { public: virtual void BaseAAA() { cout<<"BaseAAA"<<endl; } virtual void AAA() { cout<<"Base::VirtualAAA"<<endl; } }; class Derived : public Base { public: void DerivedAAA() { cout<<"DerivedAAA"<<endl; } void AAA() { cout<<"Derived::VirtualAAA"<<endl; } }; int main() { //为了实现虚函数的调用,让基类指针指向了派生类对象。 Base* pBaseOne = new Derived; //使用基类指针pBaseOne,可以访问虚函数,实现多态 pBaseOne->AAA(); //输出Derived::VirtualAAA //但是问题来了,让基类指针指向派生类后,基类指针无法访问派生类的成员了。 //pBaseOne->DerivedAAA();//操作失败 //这时需要对这个基类指针进行强制类型转换,获得派生类对象,之后操作派生类自己的成员 Derived* pDerived = dynamic_cast<Derived*>(pBaseOne); pDerived->DerivedAAA(); //输出DerivedAAA system("pause"); return 1; }
具体来说:
为了实现多态,让基类指针指向了派生类对象,但是这个基类指针无法操作派生类自己的成员了,所以就想把这个指针由基类指针转换为派生类指针,此时就可以使用dynamic_cast。即,已经有了派生类指针转换为基类指针。之后,使用dynamic_cast,把基类指针转换派生类指针。
dynamic_cast的作用:
作用:将基类类型的指针或引用安全地转换为派生类型的指针或引用。
注意事项:
(0)在dynamic_cast中,dynamic表示在类型转换时,是在运行时进行的,其与Static对应。
(1)使用dynamic_cast转换时,在expression中必有虚函数,否则编译会出错。而Static_cast没有这个限制。但是即使原来的参数和目标参数之间没有继承关系时,编译器不会报错。
原因:在类中存在虚函数时,才会有可能出现让基类指针或引用指向派生类对象的情况(虚函数使用的前提),此时转换才有意义。或者说,dynamic_cast操作时专门针对由虚函数的继承来的,它将基类指针转换为想要的子类指针,以做好操作子类中有而父类中没有的操作。由于,判断类是否有继承关系,是需要检测是否有虚函数,即在原来的参数和目标参数之间没有继承关系时,编译器不会报错,因为它由虚函数。
dynamic_cast的语法:
dynamic_cast<type-id>(expression)
说明:
(1) type_id 和 目标类型是一样的
(2) type_id 和
目标类型是引用或指针。
举例:
Derived* pDerived = dynamic_cast<Derived*>(pBaseOne);//成功,返回Derived的指针,失败返回NULL Base& BaseClass = dynamic_cast<Base&>(DerivedClass); //成功,返回Base的引用,失败抛出bad_cast异常
使用dynamic_cast转换时,会执行两个操作:
(1)它首先验证被请求的转换是否有效,这是在编译时判断,检测expression代表的类中是否有虚函数。
(2)对操作数进行类型转换。这是在运行是判断。
dynamic_cast的分类:
dynamic_cast要求要转换的两个值是有关系的,如果转型成功,则返回转换过的指针,如果转换不成功,返回NULL。
根据类之间的关系,可以分三种情况:
(1)上行转换:一个子类对象的指针/引用转换为基类的指针/引用(子类到基类的转换)
(2)下行转换:基类的指针/引用转换为一个子类的指针/引用(基类到子类的转换)
(3)交叉转换:多个基类,不同子类之间的相互转换
下面分情况说明这三种情况:
(1)上行转换:一个子类对象的指针/引用转换为基类的指针/引用(子类到基类的转换)
说明:这里dynamic_cast和static_cast以及直接赋值的效果是一样的。
举例:
#include <iostream> using namespace std; class Base { public: virtual void BaseAAA() { cout<<"BaseAAA"<<endl; } virtual void AAA() { cout<<"BaseAAA::VirtualAAA"<<endl; } }; class Derived : public Base { public: virtual void DerivedAAA() { cout<<"DerivedAAA"<<endl; } virtual void AAA() { cout<<"Derived::VirtualAAA"<<endl; } }; int main() { Derived* pD = new Derived; //正常情况-dynamic_cast Base* pB = dynamic_cast<Derived*>(pD); pB->BaseAAA(); //输出BaseAAA pB->AAA(); //输出Derived::VirtualAAA //正常情况-static_cast Base* pBB = static_cast<Derived*>(pD); pBB->BaseAAA(); //输出BaseAAA pBB->AAA(); //输出Derived::VirtualAAA //不要使用转换 Base* pBBB = pD; pBBB->BaseAAA(); //输出BaseAAA pBBB->AAA(); //输出Derived::VirtualAAA system("pause"); return 1; }
(2)下行转换:基类的指针/引用转换为一个子类的指针/引用(基类到子类的转换)
说明:这是dynamic_cast的特长。比static_cast安全。
原因:dynamic_cast能够在运行是对类型进行检查,如果绑定到引用或指针的对象不是目标类型的对象,则dynamic_cast 失败。
dynamic_cast在什么时候能成功?
如果基类指针或引用本来原来是指向或引用一个派生类的情况下,会成功。
失败后的行为:
使用dynamic_cast转换:
如果,基类指针或引用的值在转换前就是指向或引用派生类的情况下,则在转换后,派生类指针或引用就重新获得了一个派生类指针或引用。
如果,基类指针或引用的值在转换前就是指向或引用基类的情况下,在语句执行时,会立即出现错误,并返回不同的状态。
dynamic_cast失败后的状态:
如果,待转换的参数为指针类型,则返回NULL。
如果,待转换的参数为引用类型,则抛出一个bad_cast 类型的异常。
使用static_cast转换:
如果,基类指针或引用的值在转换前就是指向或引用派生类的情况下,则在转换后,派生类指针或引用就重新获得了一个派生类指针或引用。
如果,基类指针或引用的值在转换前就是指向或引用基类的情况下,在语句执行时,不会立即出现错误,而是会在使用的时候才会出现错误。
static_cast失败的情况:
如果,使用转换后的指针或引用调用了派生类的成员,则会报错。
如果,仍然调用基类(自己)的成员,则不会报错。即会隐含错误。
举例:
#include <iostream> using namespace std; class Base { public: virtual void BaseAAA() { cout<<"BaseAAA"<<endl; } virtual void AAA() { cout<<"BaseAAA::VirtualAAA"<<endl; } }; class Derived : public Base { public: virtual void DerivedAAA() { cout<<"DerivedAAA"<<endl; } virtual void AAA() { cout<<"Derived::VirtualAAA"<<endl; } }; int main() { Derived* pD = NULL; Base* pB = new Derived; //正常情况-dynamic_cast pD = dynamic_cast<Derived*>(pB); pD->DerivedAAA(); //输出DerivedAAA pD->AAA(); //输出Derived::VirtualAAA //正常情况-static_cast Derived* pD1 = static_cast<Derived*>(pB); pD1->DerivedAAA(); //输出DerivedAAA pD1->AAA(); //输出Derived::VirtualAAA Base* pBB = new Base; //失败情况 Derived* pDD = dynamic_cast<Derived*>(pBB);//此时pDD返回NULL //pDD->DerivedAAA(); //出错 //pDD->AAA(); //出错 //失败情况-static_cast Derived* pDD1 = static_cast<Derived*>(pBB); //pDD1->DerivedAAA(); //出错-用到不是自己的成员时,且该成员是虚函数时才报错。 pDD1->AAA(); //输出Base::VirtualAAA system("pause"); return 1; }
B要有虚函数,否则会编译出错;static_cast则没有这个限制。
B中需要检测有虚函数的原因:类中存在虚函数,就说明它有想要让基类指针或引用指向派生类对象的情况,此时转换才有意义。
这是由于运行时类型检查需要运行时类型信息,而这个信息存储在类的虚函数表(关于虚函数表的概念,详细可见<Inside c++ object model>)中,只有定义了虚函数的类才有虚函数表,没有定义虚函数的类是没有虚函数表的。
(3)交叉转换:多个基类,不同子类之间的相互转换
说明:dynamic_cast可以做,但是static_cast不可以做,编译都通不过。
举例:
dynamic_cast在什么时候能成功转换?
如果一个派生类DerivedTwo是由两个基类BaseOne和BaseTwo派生。
而且把DerivedTwo对象的指针给了其中一个基类BaseOne,那么可以对BaseOne的指针进行dynamic_cast转换,转换为BaseTwo的指针。
BaseOne* pBaseOne = new DerivedTwo; BaseTwo* pBaseTwo = dynamic_cast<BaseTwo*>(pBaseOne); pBaseTwo->AAA(); //正确执行 pBaseTwo->BaseTwoAAA(); //正确执行
为什么dynamic_cast能成功转换?
这是由于在定义指针pBaseOne的指向时,其指向的内容本来就包含两个基类的内容,所以可以转换成功。
给出不成功的情况:
BaseOne* pBaseOne = new DerivedOne; BaseTwo* pBaseTwo = dynamic_cast<BaseTwo*>(pBaseOne); pBaseTwo->AAA();//报错 pBaseTwo->BaseTwoAAA();//报错
dynamic_cast不能转换的原因?
这是由于在定义指针pBaseOne的指向时,其指向的内容只包含一个基类BaseOne的内容,不含有BaseTwo的内容,所以不能成功,此时pBaseTwo的值为NULL。
举例:
#include <iostream> using namespace std; class BaseOne { public: virtual void BaseOneAAA() { cout<<"BaseOneAAA"<<endl; } virtual void AAA() { cout<<"BaseOne::VirtualAAA"<<endl; } }; class BaseTwo { public: virtual void BaseTwoAAA() { cout<<"BaseTwoAAA"<<endl; } virtual void AAA() { cout<<"BaseTwo::VirtualAAA"<<endl; } }; class DerivedOne : public BaseOne { public: virtual void DerivedOneAAA() { cout<<"DerivedOneAAA"<<endl; } void AAA() { cout<<"DerivedOne::VirtualAAA"<<endl; } }; class DerivedTwo : public BaseOne,public BaseTwo { public: virtual void DerivedTwoAAA() { cout<<"DerivedTwoAAA"<<endl; } void AAA() { cout<<"DerivedTwo::VirtualAAA"<<endl; } }; int main() { BaseOne* pBaseOne = NULL; BaseTwo* pBaseTwo = NULL; //dynamic_cast转换成功的情况 pBaseOne = new DerivedTwo; pBaseTwo = dynamic_cast<BaseTwo*>(pBaseOne); pBaseTwo->AAA(); //输出DerivedTwo::VirtualAAA pBaseTwo->BaseTwoAAA(); //输出BaseTwoAAA //dynamic_cast转换失败的情况 pBaseOne = new DerivedOne; pBaseTwo = dynamic_cast<BaseTwo*>(pBaseOne); pBaseTwo->AAA(); pBaseTwo->BaseTwoAAA(); ////static_cast转换失败的情况 ////直接是编译不通过:static_cast无法实现BaseOne* 到BaseTwo* 的转换。 //pBaseOne = new DerivedOne; //pBaseTwo = static_cast<BaseTwo*>(pBaseOne); //pBaseTwo->AAA(); //pBaseTwo->BaseTwoAAA(); //pBaseOne = new DerivedTwo; //pBaseTwo = static_cast<BaseTwo*>(pBaseOne); //pBaseTwo->AAA(); //pBaseTwo->BaseTwoAAA(); system("pause"); return 1; }
参考文章:http://www.cnblogs.com/weidagang2046/archive/2010/04/10/1709226.html