MSDN:对于MFC中每个从CObject派生的类来说,都有一个相关的CRuntimeClass结构体,在程序运行时可以访问该结构体来获取对象及其基类的运行时信息。
CRuntimeClass是一个结构体,并且其本身并没有基类。在运行时确定一个对象的类型是很重要的,尤其是在做类型检查时;而C++语言本身并不支持运行时类信息。CObject和CRuntimeClass是MFC中两个非常重要的类/结构,绝大部分MFC类都是以CObject做为基类, CRuntimeClass结构同CObject密不可分,了解它们对于深入理解MFC具有重要意义。
CRuntimeClass是一个结构体,它的定义如下:
struct CRuntimeClass
{
// Attributes
//类名,一般是指包含CRuntimeClass对象的类的名称
LPCSTR m_lpszClassName;
//包含CRuntimeClass对象的类sizeof的大小,不包括它分配的内存
int m_nObjectSize;
// schema number of the loaded class分类编号(对不可分类的类,该值为-1)
UINT m_wSchema;
// NULL => abstract class 指向一个建立实例的构造函数,创建一个类的对象,抽象类则返回NULL(只有//在类支持动态创建时才有效;否则,返回NULL)。PASCAL不用管,是个过时了的calling convension。
CObject* (PASCAL* m_pfnCreateObject)();
//mfc用于动态对象创建的CRuntimeClass结构的一个成员变量
CObject*(* m_pfnCreateObject)();
#ifdef _AFXDLL
//是一个指向函数的指针,该函数返回基类的CRuntimeClass结构。
CRuntimeClass* (PASCAL* m_pfnGetBaseClass)();
#else
//m_pBaseClass的指针(函数)是MFC运行时确定类层次的关键,它一个简单的单向链表
CRuntimeClass* m_pBaseClass;
#endif
// Operations
/*
这个函数给予CObject 派生类运行时动态建立的能力
从CObject派生的类可以支持动态创建,这是在运行时创建一个指定类的对象的能力。例如,文档,视和框架类就应该支持动态创建。CreateObject成员函数可以用来实现这个功能,在运行时为这些类创建对象。
*/
CObject* CreateObject();
/*
这个函数使用 m_pBaseClass或 m_pfnGetBaseClass遍历整个类层次确定是否pBaseClass指向的类是基类,使用它可以判断某类是否是从pBaseClass指向的类在派生来。
*/
BOOL IsDerivedFrom(const CRuntimeClass* pBaseClass) const; //判断是不是一个基类的子类
// Implementation
void Store(CArchive& ar) const;
static CRuntimeClass* PASCAL Load(CArchive& ar, UINT* pwSchemaNum);
//单向链表,每个类的CRuntimeClass结构体对象的 m_pNextClass 都指向其直接基类的CRuntimeClass结构体对象
CRuntimeClass* m_pNextClass; // linked list of registered classes
};
UML图如下所示
每一个类拥有这样一个CRuntimeClass成员变量,并且有一定的命名规则(例如在类名称之前冠以“class”作为它的名称),然后通过某种手段将整个类库构建好,“类别型录网”能呈现类似的风貌
CRuntimeClass与CObject关联密切。所以必须介绍一下CObject。它是MFC类的大多数类的基类,主要是通过它实现:
(1)、运行类信息;(2)、序列化;(3)、对象诊断输出;(4)、同集合类相兼容;
1、运行时类信息:
注意:要想使用CRuntimeClass结构得到运行时类信息,你必须在你的类中包括DECLARE_DYNAMIC/IMPLEMENT_DYNAMIC、 DECLARE_DYNCREATE/IMPLEMENT_DYNCREATE或DECLARE_SERIAL/IMPLEMENT_SERIAL。但你的类又必须是从CObject派生的才能使用这些宏, 因为通过DECLARE_DYNAMIC将定义一个实现如下的函数:
CRuntimeClass* PASCAL B::_GetBaseClass()
{
return RUNTIME_CLASS(base_name);
}
该函数中的RUNTIME_CLASS是这样定义的
#define RUNTIME_CLASS( class_name ) \
(CRuntimeClass *)(&class_name::class##class_name);
即得到类中的CRuntimeClass对象指针。显而易见,如果没有基类你用IMPLEMENT_DYNAMIC时将得到一个编译错误。 除非你象CObject一样不用DECLARE_DYNAMIC而定义和实现了这些函数,CObject中的GetBaseClass只是简单的返回NULL。 实际的DECLARE_DYNAMIC在afx.h中声明如下:
#define DECLARE_DYNAMIC(classname) \
protected: \
static CRuntimeClass* PASCAL _GetBaseClass(); \
public: \
//##class_name 在预处理阶段会被自动翻译为后面class_name
static const AFX_DATA CRuntimeClass class##class_name; \
virtual CRuntimeClass* GetRuntimeClass() const; \
IMPLEMENT_DYNAMIC在afx.h中定义如下:
#define IMPLEMENT_DYNAMIC(class_name, base_class_name) \
IMPLEMENT_RUNTIMECLASS(class_name, base_class_name, 0xFFFF, NULL)
#define IMPLEMENT_RUNTIMECLASS(class_name, base_class_name, wSchema, pfnNew) \
CRuntimeClass* PASCAL class_name::_GetBaseClass() \
{ return RUNTIME_CLASS(base_class_name); } \
AFX_COMDAT const AFX_DATADEF CRuntimeClass class_name::class##class_name = { \
#class_name, sizeof(class class_name), wSchema, pfnNew, \
&class_name::_GetBaseClass, NULL }; \
//重写GetRuntimeClass虚函数,这个函数最初的定义在CObject中,重写后,将返回子类自己的GetRuntimeClass变量
CRuntimeClass* class_name::GetRuntimeClass() const \
{ return RUNTIME_CLASS(class_name); } \
总而言之,就是在预处理阶段自动生成(更准确说应该是宏翻译转换)用于类运行时检查的函数和变量,主要包括获取其基类的CRuntimeClass变量的函数_GetBaseClass,类本身的CRuntimeClass静态变量定义,同时将类本身的CRuntimeClass静态变量与其基类的CRuntimeClass静态变量绑定起来,使得子类的该变量可以直接访问到基类的变量,并且这种绑定是从子类到其基类递归进行的。在运行时判断类信息时,就可以通过类的CRuntimeClass静态变量来进行对比,从而判断出该类是否是相应的类型,这便是CObject中接口函数IsKindOf函数的作用,其实现类似于下面的过程:
BOOL CObject::IsKindOf(const CRuntimeClass *pClass) const
{
CRuntimeClass *pClassThis = GetRuntimeClass();
while (pClassThis != NULL)
{
// 检查CRuntimeClass静态变量是否相同,若相同,则表示所检查的pClass是对的
if (pClassThis == pClass)
{
return TRUE;
}
pClassThis = pClassThis->m_pBaseClass;
}
return FALSE;
}
参考:
https://www.cnblogs.com/liqilei/archive/2010/09/18/1830381.html
https://blog.csdn.net/liuchen1206/article/details/7653573