RTTI: Run-Time Type Identification,就是运行时类型识别。这个功能在C++中早就有了。只不过,大家都不用,没有怎么关注过。在C#和JAVA中可能应用比较多吧。
RTTI程序可以使用基类的指针或引用来检查所指向对象的实际派生类,它提供了一个机制可以在程序运行中判断对象的类型。在对于不同的对象有不同的操作的要求有很大的帮助。但,C++中的rtti几乎完全被否定。在刚刚发布的GOOGLE C++ CODE GUIDE中也明确指出不用RTTI。这和OO的开闭原则有冲突,而且RTTI特性完全可以用多态来实现。另外如果打开RTTI特性,还会在代码中增加一些分量。这里只是探讨一下RTTI的一般机制。
C++中提供两个操作符来表示RTTI:dynamic_cast和typeid。
dynamic_cast实现在类型间的转换。主要有三种应用 upcast, downcast和void*
- upcast
class A{};
class B : public A {};
class C: public B {};
C* pc = new C;
B* pb = new B;
A* pa = dynamic_cast(pc);
这种转换很普通,看不出什么特别之处,不管是直接父类还是间接父类,都应该是当然而。pa = dynamic_cast(pb);
- downcast
class A{void virtual f()};
class C : public A{};
A* pa = new A;
A* pc = new C;
C* pac = dynamic_cast<C*>(pa);
C* pcc = dynamic_cast<C*>(pc);
这里,pac就是零值,dynamic_cast没有转换成功会返回0,而pcc就是C*的指针指向一个C对象。
注意这里需要有虚函数
- void*
class D {void virtual f()}
class E {void virtual f()}
D* pd = new D;
E* pe = new E;
void* pv = dynamic_cast<void*>(pd);
pv = dynamic_cast<void*>(pe);
这两个类是没有继承关系的,但都相同的虚函数表,所以可以转换成VOID*,并且一起管理。
typeid操作符返回type_info&
说明参数的类型信息。用法:
typeid(1.0f);
typeid(true);
A a;
typeid(A); typeid(a); typeid(&a);
type_info描述类型信息,其实这些东西都是编译要用到的,这里只是保留下来了。那么,不同的编译器对于这些信息的保留或实现会有不一样。但基本上的信息是相同的。VC里的实现:
也就是说每个类都会有这样的一些信息,如果我们不用C++提供的这一套机制,完全可以自己实现自定义的RTTI,这样就可以只对有限的几个类用RTTI,其它都不用。
MFC的实现
在MFC中struct CRuntimeClass结构来实现rtti。
struct CRuntimeClass
{
LPCSTR m_lpszClassName;
int m_nObjectSize;
UINT m_wSchema;
CRuntimeClass* m_pBaseClass;
CRuntimeClass* m_pNextClass;
}
然后用两个宏,加到了类里。
DECLARE_DYNAMIC(class_name)
IMPLEMENT_DYNAMIC(class_name)
- 一个应用RTTI的例子
你可以在C++prime中找到。
两个类,有继承关系,要实现对象之间的比较,但对象之间的比较并不是所有数据的比较,是全部数据的一个子集。
class Base
{... forBase; // member variable}
class Derived: public Base
{... forDerived; // member variable}
这样可能需要4个函数才能实现
bool operator==(const Base&, const Base&)
bool operator==(const Derived&, const Derived&)
bool operator==(const Derived&, const Base&);
bool operator==(const Base&, const Derived&);
如果是三层的,那可能要8个函数才能完成。
但用了rtti情况就不同了。