C++对象模型之RTTI的实现原理( 三 )


然而在C++中即使一个类不具有多态的性质 , 仍然允许把一个派生类的指针赋值给一个基类的指针 , 所以这个错误比较隐晦 。
2、dynamic_cast运算符
把一个基类类型的指针或引用转换至继承架构的末端某一个派生类类型的指针或引用被称为向下转型(downcast) 。 dynamic_cast运算符的作用是安全而有效地进行向下转型 。
把一个派生类的指针或引用转换成其基类的指针或引用总是安全的 , 因为通过分析对象的内存布局可以知道 , 派生类的对象中必然存在基类的子对象 , 所以通过基类的指针或引用对派生类对象进行的所有基类的操作都是合法和安全的 。 而向下转型有潜在的危险性 , 因为基类的指针可以指向基类对象或其任何派生类的对象 , 而该对象并不一定是向下转型的类型的对象 。 所以向下转型遏制了类型系统的作用 , 转换后对指针或引用的使用可能会引发错误的解释或腐蚀程序内存等错误 。
例如对于以下的类定义:
class X{public:X(){mX = 101;}virtual ~X(){}private:int mX;}; class XX : public X{public:XX():X(){mXX = 1001;}virtual ~XX(){}private:int mXX;}; class YX : public X{public:YX(){mYX = 1002;}virtual ~YX(){}private:int mYX;};使用如下的测试代码 , 其中的类型转换均为向下转型:int main(){ X x; XX xx; YX yx; X *px =cout << px << endl; XX *pxx = dynamic_cast(px); // 转换1 cout << pxx << endl; YX *pyx = dynamic_cast(px); // 转换2 cout << pyx << endl; pyx = (YX*)px; // 转换3 cout << pyx << endl; pyx = static_cast(px); // 转换4 cout << pyx << endl; return 0;}
其运行结果如下:
C++对象模型之RTTI的实现原理文章插图
运行结果分析
px是一个基类(X)的指针 , 但是它指向了派生类XX的一个对象 。 在转换1中 , 转换成功 , 因为px指向的对象确实为XX的对象 。 在转换2中 , 转换失败 , 因为px指向的对象并不是一个YX对象 , 此时dymanic_cast返回NULL 。 转换3为C风格的类型转换而转换4使用的是C++中的静态类型转换 , 它们均能成功转换 , 但是这个对象实际上并不是一个YX的对象 , 所以在转换3和转换4中 , 若继续通过指针使用该对象必然会导致错误 , 所以这个转换是不安全的 。
从上述的结果可以看出在向下转型中 , 只有dynamic_case才能实现安全的向下转型 。 那么dynamic_case是如何实现的呢?有了上面typeid和虚函数表的知识后 , 这个问题并不难解释了 , 以转换1为例 。
1)计算指针或引用变量所指的对象的虚函数表的type_info信息 , 如下:
*(type_info*)px->vptr[-1]
2)静态推导向下转型的目标类型的type_info信息 , 即获取类XX的type_info信息
3)比较1)和2)中获取到的type_info信息 , 若2)中的类型信息与1)中的类型信息相等或是其基类类型 , 则返回相应的对象或子对象的地址 , 否则返回NULL 。
【C++对象模型之RTTI的实现原理】引用的情况与指针稍有不同 , 失败时并不是返回NULL , 而是抛出一个bad_cast异常 , 因为引用不能参考NULL 。