MFC类别型录网之执行期类型识别

17 篇文章 1 订阅
9 篇文章 6 订阅

我们知道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,成功学习完“类别型录”网中执行期类型识别功能。


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值