RTTI指运行时类型识别。设计RTTI,就要在类建构起来的时候,记录必要的信息,以建立型录。型录中的类信息,最好以链表方式连接起来,将来方便一一比较。
用CRuntimeClass描述,那是一个结构,其中至少需要有类名称、链表的Next指针、以及链表的First指针。由于First指针属于全局变量,所以它应该以static修饰。
1.定义一个类别型录结构
struct CRuntimeClass
{
//Attributes
LPCSTR m_lpszClassName; //类名,一般是指包含CRuntimeClass对象的类的名称
int m_nObjectSize; //包含CRuntimeClass对象的类sizeof的大小,不包括它分配的内存
UINT m_wSchema; // schema number of the loaded class分类编号(对不可分类的类,该值为-1)
CObject* (PASCAL * m_pfnCreateObject)();// NULL => abstract class 指向一个建立实例的构造函数,
//创建一个类的对象,抽象类则返回NULL(只有在类支持动态创建时才有效;否则,返回NULL)。
//PASCAL不用管,是个过时了的calling convension。 CObject*(* m_pfnCreateObject)();
//指向函数的指针 如:int (*fn)();
//这是mfc用于动态对象创建的CRuntimeClass结构的一个成员变量
CRuntimeClass* m_pBaseClass;//CRuntimeClass objects linked together in simple list
static CRuntimeClass* pFirstClass;//型录类的地址指针
CRuntimeClass* m_pNextClass;//下个型录类的指针
};
2.DECLARE_DYNAMIC宏
此宏在每个类中声明了一个类别型录类和一个虚函数来得到这个型录的地址指针
#define DECLARE_DYNAMIC(class_name)\
public:\
static CRuntimeClass class##class_name;\ //定义一个类别型录类,注意是静态变量
virtual CRuntimeClass* GetRuntimeClass() const;\ //得到这个型录(CRuntimeClass类)的指针,
//是虚函数
- 宏定义中的##
把两个字符串连在一起
全部的动态声明宏的定义如下:
DECLARE_DYNAMIC(CCmdTarget)
DECLARE_DYNAMIC(CWinThread)
DECLARE_DYNAMIC(CWinApp)
DECLARE_DYNAMIC(CDocument)
DECLARE_DYNAMIC(CWnd)
DECLARE_DYNAMIC(CFrameWnd)
DECLARE_DYNAMIC(CView)
相当于在每个类中声明了一个静态变量和虚函数:
class CCmdTarget:public CObject
{
public:
static CRuntimeClass classCCmdTarget;
virtual CRuntimeClass* GetRuntimeClass() const;
}
class CWinThread:public CCmdTarget
{
public:
static CRuntimeClass classCWinThread;
virtual CRuntimeClass* GetRuntimeClass() const;
}
class CWinApp:public CWinThread
{
public:
static CRuntimeClass classCWinApp;
virtual CRuntimeClass* GetRuntimeClass() const;
}
class CDocument:public CCmdTarget
{
public:
static CRuntimeClass classCDocument;
virtual CRuntimeClass* GetRuntimeClass() const;
}
class CWnd:public CCmdTarget
{
public:
static CRuntimeClass classCWnd;
virtual CRuntimeClass* GetRuntimeClass() const;
}
class CFrameWnd:public CWnd
{
public:
static CRuntimeClass classCFrameWnd;
virtual CRuntimeClass* GetRuntimeClass() const;
}
class CView:public CWnd
{
public:
static CRuntimeClass classCView;
virtual CRuntimeClass* GetRuntimeClass() const;
}
3.IMPLEMENT_DYNAMIC
此宏主要在于给tatic CRuntimeClass* pFirstClass;//型录类的地址指针,CRuntimeClass* m_pNextClass;//下个型录类的指针赋值,串联起类别型录网。
#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)
//形参的内容如下:
//当前类的类名,当前类的基类名 ,分类编号65535,函数指针为空
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};\ //RUNTIME_CLASS是宏,相当于基类的地址,所有的CRuntinmeClass都是类中的静态变量,地址都是随时可以取到
static AFX_CLASSINIT _init_##class_name(&class_name::class##class_name);\ //将各个CRuntinmeClass串联起来,是形式上的串联,逻辑上没联系。
CRuntimeClass* class_name::GetRuntimeClass()const\ //此函数也是返回CRuntinmeClass的地址,注意const,此函数不能修改类中的成员变量
{return &class_name::class##class_name;}\
- 宏定义中的#
相当于“ ”
以CView为例,上述的宏代码相当于
static char _lpszCView[]=“CView”;//用一个全局的字符串数组来记录类名
#define RUNTIME_CLASS(class_name)
(&class_name::class##class_name)
CRuntimeClass CView::classCView={
_lpszCView,sizeof(CView),0xFFFF,NULL,
&CWnd::classCWnd,NULL};
//DECLARE_DYNAMIC(CView)所声明的静态变量
//static CRuntimeClass classCView赋值
//只有6个变量需要赋值,static CRuntimeClass* pFirstClass;是全局变量
static AFX_CLASSINIT _init_CView(&CView::classCView);
AFX_CLASSINIT::AFX_CLASSINIT(CRuntimeClass* pNewClass)
{
pNewClass->m_pNextClass=CRuntimeClass::pFirstClass;
CRuntimeClass::pFirstClass=pNewClass;
}
串联起前后链表的关系
CRuntimeClass* CView::GetRuntimeClass() const
{return &CView::classCView;}