MFC中RTTI( 运行时类型识别 )的模拟实现
在编程的过程中,我们经常要判断一个对象是否属于某个类型,这就是RTTI,运行时类型识别,在MFC中RTTI是怎么实现的呢,主要是借助于一个类CRuntimeClass,两个宏
DECLARE_DYNAMIC和 IMPLEMENT_DYNAMIC .
我们在每个类中加入一个CRuntimeClass对象,这些对象被用链表的形式链接起来,形成一个RTTI 网。
DECLARE_DYNAMIC和IMPLEMENT_DYNAMIC两个宏,前者实现的就是在类中加入CRuntimeClass对象,后者将它们链接起来。
CRuntimeClass 是一个结构,其中包含类名称,下一个CRuntimeClass 的指针,以及static 的CRuntimeClass 链表头指针 等等。
1. CRuntimeClass的定义模仿如下:
struct CRuntimeClass
{
LPCSTR m_lpszClassName;//用来保存类的名称
Int m_nObjectSize;// 类对象的大小
UINT m_wschema;
CObject * (PASCAL * pfnCreateObject) ();
CRuntimeClass * m_pBaseClass; //指向基类的CRuntimeClass 的指针
static CRuntimeClass * pFirstClass; //静态成员,整个链表的头结点
CRuntimeClass * m_pNextClass; //下一个 CRuntimeClass结点
};
2. DECLARE_DYNAMIC 的定义如下:
#define DECLARE_DYNAMIC(clss_name)/
public:/
static CRuntimeClass class##class_name;/
virtual CRuntimeClass * GetRuntimeClass() const;
这里##是个连接符,class##class_name表示在class_name前面加上字符class
例如 :class##CView 则表示 classCView
这个宏定义了两个东西:一个CRuntimeClass 的静态成员变量 class##class_name
一个GetRuntimeClass的虚函数,函数返回本类的CRuntimeClass * .
3. 定义好了,现在就要实现并且链接起来,这就是IMPELEMENT_DYNAMIC 宏:
#define IMPELEMENT_DYNAMIC (class_name,base_class_name)/
_IMPELEMENT_RUNTIMECLASS/ (class_name,base_class_name,0xFFFF,NULL)
_IMPELEMENT_RUNTIMECLASS 又是一个宏。它定义如下:
#define _IMPELEMENT_RUNTIMECLASS/ (class_name,base_class_name,wSchema,pfnNew)/
先定义一个包含类名字符串的字符数组
static _lpsz##class_name[]=#class_name;
初始化 CRuntimeClass 成员结构体
CRuntimeClass class_name::class##class_name={/
_lpsz##class_name,/
sizeof(class_name),/
wSchema,/
pfnNew,/
RUNTIME_CLASS(base_class_name),/
NULL}; /
一个重要的宏对象
static AFX_CLASSINIT _init_##class_name/
(&class_name::class##class_name);/
实现类的GetRuntimeClass虚函数
CRuntimeClass * class_name::GetRuntimeClass() const/
{ return &class_name::class##class_name;}
重点在于AFX_CLASSINIT ,它有一个构造函数,它定义如下:
struct AFX_CLASSINIT
{
AFX_CLASSINIT(CRuntimeClass * pNewClass)
{
pNewClass->m_pNextClass=CRuntimeClass::pFirstClass;
CRuntimeClass::pFirstClass=pNewClass;
}
}
哈哈,一个完美的前插入新结点到链表,这样整个链表就被链接建立起来了。
但是,链表的头需要特殊处理,所以CObject不能套用DECLARE_DYNAMIC和
IMPELEMENT_DYNAMIC 宏,它必须特殊处理如下:
class CObject
{
public:
CRuntimeClass * GetRuntimeClass() const;
.........
public:
static CRuntimeClass classCObject;
.........
};
CObject实现如下:
static char szCObject[]="CObject";
struct CRuntimeClass CObject::classCObject=
{ szCObject,sizeof(CObject),0xffff,NULL,NULL,NULL};
static AFX_CLASSINIT _init_CObject(&CObject::classCObject);
CRuntimeClass * GetRuntimeClass() const
{
return &CObject::classCObject;
}
这里最主要的是,将CObject::classCObject的基类CRuntimeClass *设置为NULL,因为它本身就是基类。
最后,记得初始化整个链表的头指针:
CRuntimeClass::pFirstClass=&CObject::classCObject;
让它指向CObject的CRuntimeClass对象。
那么有了这些,我们怎么来判断一个对象是否属于某个类型呢?
我们为CObject类加上一个IsKindOf方法,这样所有的子类都会继承这个方法:
class CObject
{
public:
........
BOOL IsKindOf(const CRuntimeClass * pClass)
{
//首先获得类本身的CRuntimeClass
CRuntimeClass *pClassThis=GetRuntimeClass();
//然后逆流而上查找基类,查找是否有CRuntimeClass跟传入参数相同,如是,则说明是属于此类型
while(pClassThis!=NULL)
{
if(pClassThis==pClass)
{
return TRUE;
}
pClassThis=pClassThis->m_pBaseClass;
}
return FALSE;
}
}
这样我们就可以以如下方式来进行RTTI ,运行时类型识别:
#define RUNTIME_CLASS(class_name) /
& class_name::class##class_name;
CView * pView=new CView;
pView->IsKindOf(RUNTIME_CLASS(CView)); 应该是TRUE
pView->IsKindOf(RUNTIME_CLASS(CWnd)); 应该是TRUE
pView->IsKindOf(RUNTIME_CLASS(CDocument)); 应该是FALSE
至此,RTTI 的模拟到此结束,虽然整个过程都是深入浅出中描述的,但是自己单独把这个过程给描述出来,直接增加了对RTTI的理解深度。
下一篇博客,动态创建,也是基于RTTI的结果。