如果没有继承,类只是具有一些相关行为的数据结构,这只是对过程语言的一大改进,而继承则开辟了完全不同的新天地。通过继承,可以在已有类的基础上创建新类。这样,类就成为可重用和可扩展的组件。博主的《漫谈继承技术》系列博文将讲述各种利用继承功能的方法。学习继承的语法,和利用继承的一些复制技术。本篇博文先给大家介绍一下dynamic_cast在继承中的使用,希望对大家加深对继承技术的理解有一定的帮助。
dynamic_cast
语法:dynamic_cast < type-id> ( expression )
该运算符把expression转换成type-id类型的对象。type-id必须是类的指针、类的引用或者void *。
dynamic_cast的转换是在运行时进行的,它的一个好处是会在运行时做类型检查。如果对象的类型不是期望的类型,它会在指针转换的时候返回nullptr,而在引用转换的时候会抛出std::bad_cast标准异常。
dynamic_cast一般只在基类指向派生类对象的指针之间或引用之间进行类型转换。dynamic_cast运行时类型检查需要运行时类型信息,而这个信息存储在类的虚函数表中。如果没有虚函数表则会报错。只要基类有一个虚函数,编译器就会产生一个虚表指针指向虚函数表,虚函数表中存储的是虚函数的入口地址。dynamic_cast在运行时遍历继承树,只要是在继承树上的类都可以相互转换。咱们举个简单的栗子吧。
#include <iostream>
using namespacestd;
struct A
{
virtualvoidf() { }
};
struct B: public A{ };
struct C{ };
void fun()
{
A a;
B b;
A* ap = &b;
//指针转换
B* b1 = dynamic_cast<B*>(&a); //nullptr, because 'a' is not a 'B'
if(nullptr== b1)
{
cout<< "b1 is nullptr"<<endl;
}
B* b2 = dynamic_cast<B*>(ap); //'b'
if(nullptr== b2)
{
cout<< "b2 is nullptr"<< endl;
}
C* c = dynamic_cast<C*>(ap); //nullptr
if(nullptr== c)
{
cout<< "c is nullptr"<< endl;
}
//可以将void*作为中间类型,用来存储实际的类类型指针
//待需要的时候再提供reinterpret_cast转换回去
void*pv = dynamic_cast<void*>(ap);
//dynamic_cast的操作数必须是指向完整类类型的指针或引用
//B*pb = dynamic_cast<B*>(pv);
A *pa = reinterpret_cast<A*>(pv);
consttype_info& timeinfo = typeid(*pa);
cout<< "timeinfo: "<< timeinfo.name() << endl;
//引用转换
A& ar = dynamic_cast<A&>(*ap); // Ok.
B& br = dynamic_cast<B&>(*ap); // Ok.
//C&cr = dynamic_cast<C&>(*ap); // std::bad_cast
}
int main(intargc,char**argv)
{
fun();
return0;
}
程序运行结果:
如果你使用的是VC 6.0,可能回报以下警告信息,那是因为你还没用打开动态类型信息支持。使用'dynamic_cast'需要打开run-time type information 支持,否则会报以上警告。
打开方式:菜单project -> setting ->c /c++ -> c++ language -> enable run-time type information (RTTI)(勾选此项)
以上是VC 6.0 打开动态类型信息支持的方式,VS是自动支持的,就不用再打开。
注意:仅在必要的时候才使用向下转型,一定要使用dynamic_cast。举个栗子。
#include <iostream>
using namespacestd;
struct A
{
virtualvoidf() { }
};
struct B: public A{ };
struct C: public A{ };
void fun()
{
//基类引用
A &refa = A();
A &refb = B();
A &refc = C();
//初始化派生类指针
C *pc = nullptr;
//向下转型
//基类A对象指针向派生类C指针转型,此转型是不安全的,返回nullptr
pc = dynamic_cast<C*>(&refa);
if(nullptr== pc)
{
cout<< "pc = dynamic_cast<C*>(&refa), pc isnullptr" << endl;
}
//派生类B对象指针向派生类C指针转型,此转型是不安全的,返回nullptr
pc = dynamic_cast<C*>(&refb);
if(nullptr== pc)
{
cout<< "pc = dynamic_cast<C*>(&refb), pc isnullptr" << endl;
}
//派生类C对象指针向派生类C指针转型,此转型是安全的
pc = dynamic_cast<C*>(&refc);
if(nullptr== pc)
{
cout<< "pc = dynamic_cast<C*>(&refc), pc isnullptr" << endl;
}
//平行转型
B *pB = new B();
C *pC = new C();
pc = nullptr;
//派生类B对象指针向派生类C指针转型,此转型是不安全的,返回nullptr
pc = dynamic_cast<C*>(pB);
if(nullptr== pc)
{
cout<< "pc = dynamic_cast<C*>(pB), pc isnullptr" << endl;
}
//释放资源
deletepB;
pB = nullptr;
deletepC;
pC = nullptr;
}
int main()
{
fun();
return0;
}
程序运行结果:
注意到了么?dynamic_cast拒绝没有意义的转型
向上转型是安全的,如果是对象的话,会发生对象切片的情况,但这是安全的,只是不能再通过转换后的基类对象调用派生类成员方法和使用派生类数据成员。那怎么才能避免对象切片呢?使用基类指针或者引用就可以避免。这部分代码我就不敲了,大家自己敲代码试试吧。
如果想了解更多关于继承技术相关的知识,请关注博主《漫谈继承技术》系列博文,相信你能够在那里寻找到更多有助你快速成长和深入你对继承相关的知识和一些复制的技术理解和掌握。