MFC框架早在标准C++之间提出并实现了类的运行时识别(RTTI)功能,下面记录下基于我对其的理解。
要实现RTTI必须在定义的时候记录下来类的基本信息。MFC构建了一个CRuntimeClass的结构体用以保存类的基本信息。CRun提么Class的定义如下:
struct CRuntimeClass
{
LPCSTR m_pszClassName; //类名称
int m_nObjectSize; //类的大小
UINT m_wSchema;
CObject (PASCAL * m_pfnCreateObject)();
CRuntimeClass *m_pBaseClass; //类的基类(父类)
static CRuntimeClass *pFirstClass;//保存类信息的链表头指针
CRuntimeClass *m_pNextClass;//链表的下一个
};
在定义好这样一个结构体后,在每个类中添加一个这样的成员(静态的)即可。这样的每个实例都可以通过该属性访问到类的相关信息。这样的定义是属于类的所有对象共有的,在定义类的时候就已经定义好了,所以只需要将该属性定义为静态的即可。通过这种做法每个类都保存的类的所有相关信息,但是如果要查找类的相关信息是不够的,所以通过链表的方式将这些类的CRuntimeClass静态属性组织起来,这样可以遍历程序中的所有类,也可以方便查找了。
其具体实现方式为,在MFC框架中定义个DECLARE_DYNAMIC(class_name)的宏,使用宏来实现自动添加该成员(属性)。该宏的具体定义为:
#define DECLARE_DYNAMIC(class_name) \ //class_name为类的名称
public: \
static CRuntimeClass class##class_name;\ //定义类中的静态成员 ,将该成员的名称固定为class+类名称的形式
virtual CRuntimeClass *GetRuntimeClass() const;//定义一个类的返回类相关信息的函数,该函数返回一个CRuntimeClass的指针,该指针指向类的静态成员变量(及前面定义的CRuntimeClass class##class_name)
上面的步骤定义了类中的CRuntimeClass,但是并未提供相应的实现。所以MFC框架继续定义了一个IMPLEMENT_DYNAMIC(class_name,base_class_name)的宏用以实现。其具体定义为:
struct AFX_CLASSINIT
{
AFX_CLASSINIT(CRuntimeClass *pNewClass);
}; //定义该结构体主要是为了下面的宏定义中修改保存类信息链表的相关信息,将当前类的静态成员CRuntimeClass添加到链表头。
#define RUNTIME_CLASS(class_name) \
(&class_name::class##class_name)\\用于下面的宏中返回类中静态成员CRuntimeClass的地址。
#define _IMPLEMENT_RUNTIMECLASS(class_name,base_class_name,wSchema,pfnNew) \
static char _lpsz##class_name[]=#class_name;\ //定义一个文件域的变量用以保存类的名称(字符串常量)
CRuntimeClass class_name::class##class_name={\
_lpsz##class_name,sizeof(class_name),wSchema,pfnNew,RUNTIME_CLASS(base_class_name),NULL};\ \\定义类中的静态成员CRuntimeClass class##class_name,这里面包括类的名称,类的大小,父类等。这里需要注意的是这里的定义并没有改变该结构体中的静态成员变量——指向保存类信息的链表头。
static AFX_CLASSINIT _init_##class_name(&class_name::class##class_name);\ //定义另一个结构体通过使用该结构的构造方法来实现将链表的头指针指向当前类的CRuntimeClass,将下一个指针指向链表中以前的头,这样就将所有类中的CRuntimeClass信息通过链表连接起来。
CRuntimeClass *class_name::GetRuntimeClass() const \
{return &class_name::class##class_name;}\ \\定义类中的获取成员变量CRuntimeClass的具体实现。
#define IMPLEMENT_DYNAMIC(class_name,base_class_name) \
_IMPLEMENT_RUNTIMECLASS(class_name,base_class_name,0XFFFF,NULL)
上面的这些就是MFC种的RTTI的仿真。需要注意的是,CRuntimeClass的链表中,利用了在链表头插入的方式(对于单链表而言,这应该比较快捷的方法),所以最后添加的类的相关信息在链表的标头位置。另外还需要注意的,这里链表的头在初始化的时候并没有初始化,所以对于所有类的父类及CObject类而言,不能通过简单的DELCARE_DYNAMIC、IMPLEMENT_DYNAMIC宏来实现。
来源:oschina
链接:https://my.oschina.net/u/188882/blog/101907