原文:https://www.cnblogs.com/renzhuang/articles/10466420.html
参考:https://www.cnblogs.com/QG-whz/p/4517336.html
- dynamic_cast主要用于“安全地向下转型”
dynamic_cast用于类继承层次间的指针或引用转换。主要还是用于执行“安全的向下转型(safe downcasting)”,也即是基类对象的指针或引用转换为同一继承层次的其他指针或引用。至于“先上转型”(即派生类指针或引用类型转换为其基类类型),本身就是安全的,尽管可以使用dynamic_cast进行转换,但这是没必要的, 普通的转换已经可以达到目的,毕竟使用dynamic_cast是需要开销的。
#include <iostream> using namespace std; class Base { public: Base() {}; virtual void Show() { cout << "This is Base calss"; } }; class Derived :public Base { public: Derived() {}; void Show() { cout << "This is Derived class"; } void Dfun() { cout << "Dfun"; } }; int main11() { Base *base; Derived *der = new Derived; //base = dynamic_cast<Base*>(der); //正确,但不必要。 base = der; //先上转换总是安全的 base->Show(); // This is Derived class 子类转父类,安全,且转换后,还是调用子类的虚函数, //base->Dfun(); //ERR: Dfun不是Base的成员 子类转父类,安全,但是转换后,不能再使用子类的非虚函数, return 0; }
- dynamic_cast与继承层次的指针
对于“向下转型”有两种情况。一种是基类指针所指对象是派生类类型的,这种转换是安全的;另一种是基类指针所指对象为基类类型,在这种情况下dynamic_cast在运行时做检查,转换失败,返回结果为0;
#include <iostream> using namespace std; class Base { public: Base() {}; virtual void Show() { cout << "This is Base calss"<<endl; } }; class Derived :public Base { public: Derived() {}; virtual void Show() { cout << "This is Derived class"<<endl; } void Dfun() { cout << "Dfun"<<endl; } char name[10]; }; int main222() { //场景一: Base *base = new Base; //Derived *der = base; //error C2440 : “初始化”: 无法从“Base * ”转换为“Derived *” Derived *der = dynamic_cast<Derived*>(base); //执行完后 der是空指针,所以这个转换时无意义的 //der->Show(); //编译没问题,运行就会报 der是nulptr的段错误。 der->Dfun(); //OK 实际上同下一条语句 //((Derived*)(0))->Dfun(); //OK //cout<<sizeof(((Derived*)(0))->name)<<endl; // OK 10 //正确使用场景: 父类指针指向子类对象,需要使用子类对象的非虚接口时 Base *pB = new Derived; //Derived *pD = pB; //“初始化”: 无法从“Base * ”转换为“Derived *” 解析:多态是运行时才有的, Derived *pD = dynamic_cast<Derived*>(pB); pD->Dfun(); // 如果直接使用 pB->Dfun(); 就会报错,error C2039: 'Dfun' : is not a member of 'Base' cout << sizeof(pD->name)<<endl; // OK 10 pD->Show(); return 0; }
- dynamic_cast和引用类型
在前面的例子中,使用了dynamic_cast将基类指针转换为派生类指针,也可以使用dynamic_cast将基类引用转换为派生类引用。
同样的,引用的向上转换总是安全的:
Derived c; Derived & der2= c; Base & base2= dynamic_cast<Base&>(der2);//向上转换,安全 base2.Show();
所以,在引用上,dynamic_cast依旧是常用于“安全的向下转型”。与指针一样,引用的向下转型也可以分为两种情况,与指针不同的是,并不存在空引用,所以引用的dynamic_cast检测失败时会抛出一个bad_cast异常:
int main() { //第一种情况,转换成功 Derived b ; Base &base1= b; Derived &der1 = dynamic_cast<Derived&>(base1); cout<<"第一种情况:"; der1.Show(); cout<<endl; //第二种情况 Base a ; Base &base = a ; cout<<"第二种情况:"; try{ Derived & der = dynamic_cast<Derived&>(base); } catch(bad_cast) { cout<<"转化失败,抛出bad_cast异常"<<endl; } system("pause"); }
运行结果:
- 使用dynamic_cast转换的Base类至少带有一个虚函数
当一个类中拥有至少一个虚函数的时候,编译器会为该类构建出一个虚函数表(virtual method table),虚函数表记录了虚函数的地址。如果该类派生了其他子类,且子类定义并实现了基类的虚函数,那么虚函数表会将该函数指向新的地址。虚表是C++多态实现的一个重要手段,也是dynamic_cast操作符转换能够进行的前提条件。当类没有虚函数表的时候(也即一个虚函数都没有定义),dynamic_cast无法使用RTTI,不能通过编译,报错:error C2683: “dynamic_cast”:“Base”不是多态类型。
当然,虚函数表的建立对效率是有一定影响的,构建虚函数表、由表查询函数 都需要时间和空间上的消耗。所以,除了必须声明virtual(对于一个多态基类而言),不要轻易使用virtual函数。对于虚函数的进一步了解,可以查看《Effective C++》