我们知道MFC具有运行时类型识别(RTTI)的功能,那它究竟是怎么实现的呢?在MFC的内部,它通过一个叫CRuntimeClass的数据结构以及几个特殊的宏操作来构建一个“类别型录”网,通过在运行时查询该网上的信息来判断某个对象的所属类型。
CRuntimeClass
该类定义于AFX.H中,内容如下:
struct CRuntimeClass
{
// Attributes
LPCSTR m_lpszClassName;
int m_nObjectSize;
UINT m_wSchema; // schema number of the loaded class
CObject* (PASCAL* m_pfnCreateObject)(); // NULL => abstract class
#ifdef _AFXDLL
CRuntimeClass* (PASCAL* m_pfnGetBaseClass)();
#else
CRuntimeClass* m_pBaseClass;
#endif
// Operations
CObject* CreateObject();
BOOL IsDerivedFrom(const CRuntimeClass* pBaseClass) const;
// Implementation
void Store(CArchive& ar) const;
static CRuntimeClass* PASCAL Load(CArchive& ar, UINT* pwSchemaNum);
// CRuntimeClass objects linked together in simple list
CRuntimeClass* m_pNextClass; // linked list of registered classes
};
在这里,我们需要用到的字段主要有2个。
m_lpszClassName:所属类型的类名
m_pBaseClass:指向其基类的CRuntimeClass对象
好了,需要的主要数据结构就是这样,下面来看一下几个重要宏的定义情况。
DECLARE_DYNAMIC
定义于AFX.H,内容如下:
#define DECLARE_DYNAMIC(class_name) \
public: \
static const AFX_DATA CRuntimeClass class##class_name; \
virtual CRuntimeClass* GetRuntimeClass() const;
提示:宏定义中的“##”用来告诉编译器,将这两个字符串连在一起。还有那个奇怪的AFX_DATA也是一个宏定义,不过目前只是一个占位符,也就是说它会被替换为空字符,即,可忽略。
如果我们如下使用:
DECLARE_DYNAMIC(CView)
则,编译器会为我们替换成下面的内容:
public:
static CRuntimeClass classCView;
virtual CRuntimeClass* GetRuntimeClass() const;
也就是说,只要我们在类声明的时候加入DECLARE_DYNAMIC宏,就可以使该类拥有运行时动态类型识别的数据结构和获得该结构的方法。
但是光有这个数据结构还是不行,我们得让不同类之间的该数据结构产生一定的关系,这样才能交织成一张我们需要的“网”。这个时候就需要用到另一个重要的宏。
IMPLEMENT_DYNAMIC
定义于AFX.H,内容如下:
#define IMPLEMENT_DYNAMIC(class_name, base_class_name) \
IMPLEMENT_RUNTIMECLASS(class_name, base_class_name, 0xFFFF, NULL)
而其中IMPLEMENT_RUNTIMECLASS又是一个宏,如下:
#define IMPLEMENT_RUNTIMECLASS(class_name, base_class_name, wSchema, pfnNew) \
AFX_COMDAT const AFX_DATADEF CRuntimeClass class_name::class##class_name = { \
#class_name, sizeof(class class_name), wSchema, pfnNew, \
RUNTIME_CLASS(base_class_name), NULL }; \
CRuntimeClass* class_name::GetRuntimeClass() const \
{ return RUNTIME_CLASS(class_name); }
其中AFX_COMDAT和AFX_DATADEF都是空宏,可忽略。
另一个宏RUNTIME_CLASS定义如下:
#define RUNTIME_CLASS(class_name) ((CRuntimeClass*)(&class_name::class##class_name))
由上可以看出,IMPLEMENT_DYNAMIC宏填充了CRuntimeClass结构体中的值。
当我们写下如下代码:
class CView : public CWnd
{
DECLARE_DYNAMIC(CView)
……
};
//在实现文件中
IMPLEMENT_DYNAMIC(CView, CWnd)
则,宏展开后,上述代码将变成:
class CView : CWnd
{
static CRuntimeClass classView;
virtual CRuntimeClass* GetRuntimeClass() const;
……
};
//实现文件中
const CRuntimeClass CView::classCView = {
“CView”, sizeof(class CView), 0xFFFF, NULL,
&CWnd::classCWnd, NULL };
CRuntimeClass* CView::GetRuntimeClass() const
{ return &CView::classCView; }
经过DECLARE_DYNAMIC和IMPLEMENT_DYNAMIC两个宏,我们可以构建出一个如下的运行时类型链:
在各个类中使用这两个宏可以就可以构成我们所需的“类别型录”网。当然在CObject中,它的m_pBaseClass成员将被值为NULL,以表示链网的“尽头”。
当我们使用IsKindOf函数来进行运行时类型判断时,其操作如下:
BOOL CObject::IsKindOf(const CRuntimeClass* pClass) const
{
CRuntimeClass* pClassThis = GetRuntimeClass();//返回该对象所属类的CRuntimeClass成员
while(pClassThis != NULL)
{
if(pClassThis == pClass)//相匹配
return TRUE;
pClassThis = pClassThis->m_pBaseClass;//按m_pBaseClass指向继续向前搜寻
}
return FALSE;
}
OK,成功学习完“类别型录”网中执行期类型识别功能。