RTTI机制
C++是一种静态类型语言。其数据类型是在编译期就确定的,不能在运行时更改。然而由于面向对象程序设计中多态性的要求,C++中的指针或
引用(Reference)本身的类型,可能与它实际代表(指向或引用)的类型并不一致。有时我们需要将一个多态指针转换为其实际指向对象的类型,就需
要知道运行时的类型信息,于是产生了运行时类型识别的要求。C++要想获得运行时类型信息,可以通过RTTI机制。
RTTI(Run Time Type Identification)即通过运行时类型识别,程序能够通过基类的指针或引用获知这些指针或引用所指的对象的实际派生类型。
本来基类指针的作用就是使代码能够获得一个统一的接口,现在要逆过来,通过这个基类指针来知道它的实际派生类型,即通过一堆if来判断。
RTTI提供了两个非常有用的操作符:typeid 和 dynamic_cast
1)typeid:返回指针或引用所指对象的实际类型。它返回一个对type_info类对象的引用,要使用typeid必须使用头文件<typeinfo>。
对非引用类型,typeid是在编译时期识别的,只有引用类型才会在运行时识别。
运行时获知变量类型名称,可以使用 typeid(变量).name(),需要注意不是所有编译器都输出"int"、"float"等之类的名称,对于这类的编译器可以这样使用:
float f = 1.1f; if(typeid(f) == typeid(0.0f)) { ... }
下面举一个例子
class Base1 {}; class Derive1 : public Base1 {}; class Base2 { virtual void fun(void) {}}; class Derive2 : public Base2 {}; class Derive3 : public Base2 {}; int main(void) { cout << typeid(1.1f).name() << endl; Derive1 d1; Base1& b1 = d1; cout << typeid(b1).name() << endl; // 输出"class Base1",因为Derive1和Base1之间没有多态性 // 当类中不存在虚函数时,typeid是编译时期的事情,也就是静态类型 Derive2 d2; Base2& b2 = d2; cout << typeid(b2).name() << endl; // 输出"class Derive2",因为Derive1和Base1之间有了多态性 // 当类中存在虚函数时,typeid是运行时期的事情,也就是动态类型 Derive2* pb1 = dynamic_cast<Derive2*>(&b2); Derive3* pb2 = dynamic_cast<Derive3*>(&b2); cout << (0 != pb1) << endl; // 输出true, 因为b2本身确实是指向 Derive2 cout << (0 != pb2) << endl; // 输出false, 因为b2本身不是指向 Derive3 return 0; }
2)dynamic_cast:将一个基类对象指针(或引用)转换到继承类指针,dynamic_cast会根据基类指针是否真正指向继承类指针来做相应处理。
如果一条dynamic_cast语句的转换目标是指针类型并且转换失败了,会返回一个空指针。如果基类或者子类没有任何虚函数(如果基类有
虚函数表,子类当然是自动继承了该表),当他们作为dynamic_cast的源类型进行转换时,编译会失败。
注意:dynamic_cast转换符只能用于含有虚函数的类,其表达式为 dynamic_cast<类型>(表达式),其中的类型是指把表达式要转换成的目标类型。
Base base(“liu”); Derived de(“wang”,23); Base* pb = &de; Derived *pd = dynamic_cast<Derived*>(pb) // 会转换成功, 因为pd真指向派生类对象 // 常用形式 void f(const Base &b) { try { const Derived &d = dynamic_cast<Derived&>(b); } catch(bad_cast) { //处理类型转换失败的情况 } }
下面给出一个存储任意类型值的容器的代码:
#include <utility> #include <typeinfo> class RunTypeValueHolder { public: virtual ~RunTypeValueHolder() {} public: virtual std::type_info const& GetType() const = 0; virtual RunTypeValueHolder* GetCopy() const = 0; virtual void SetValue(RunTypeValueHolder* valueHolder) = 0; }; template <typename ValueType> class RunTypeValue : public RunTypeValueHolder { public: RunTypeValue(ValueType const& value) : _value(value) {} public: virtual std::type_info const& GetType() const { return typeid(ValueType); } virtual RunTypeValueHolder* GetCopy() const { return new RunTypeValue(_value); } void SetValue(RunTypeValueHolder* valueHolder) { _value = ((RunTypeValue<ValueType>*)valueHolder)->_value; } RunTypeValue& operator=(RunTypeValue const&) = delete; // disable copy-assignment public: ValueType _value; }; class RunType { public: RunType() : _valueHolder(NULL) {} template <typename ValueType> RunType(ValueType const& value) { _valueHolder = new RunTypeValue<ValueType>(value); } RunType(RunType const& other) { _valueHolder = NULL; if (other._valueHolder != NULL) _valueHolder = other._valueHolder->GetCopy(); } virtual ~RunType() { delete _valueHolder; } public: bool IsEmpty() const { return !_valueHolder; } template <typename ValueType> RunType& operator=(ValueType const& rhs) { if (typeid(rhs) == GetType()) ((RunTypeValue<ValueType>*)_valueHolder)->_value = rhs; return *this; } std::type_info const& GetType() const { if (_valueHolder != NULL) return _valueHolder->GetType(); return typeid(void); } private: RunTypeValueHolder* _valueHolder; };