深入浅出MFC:MFC中的的RTTI实现

来源:互联网 发布:新闻资讯源码 作家 编辑:程序博客网 时间:2024/06/10 02:27

在了解了MFC的基本运行的流程之后,让我们再来看看一个特殊的问题:如何在运行的过程中动态地保存一个对象的类型信息并判断得到其类型

虽然在很早之前,C++就已经原生的实现了RTTI特性,只需要通过使程序包含该头文件<typeinfo.h> ,然后使用typeid 运算符即可直接获取对象的类型,但是对于更早出现的MFC,一开始没有RTTI,所以MFC只好自身内部实现了一套“运行时自动类型识别的机制”。

对于RTTI的实现,该如何设计呢?对于一种颜色,如果想要知道他的RGB成分,没有供你查找的RGB表可以做到么?对于一个没学过英语的人,一个英语单词不通过词典能知道它的意思么?对于对象的类型,也是一样的,所以我们需要建立这样一个数据结构来保存我们所有的类型,而且我们需要在对象建立起来的时候就实现该类型的记录,对于数据结构的选择这里我们跟随侯俊杰先生的想法,选择链表,这有利于我们快速的遍历查询。

在我们建立这样一条链表之前,我们需要预先设计一个用来储存相关类型信息的单元结构:

这里引用《深入浅出MFC》中的一段实现定义:

struct  CRuntimeClass{LPCSTR m_lpszClassName;  // LOCSTR = char*int m_nObjectSize;UINT m_wSchema; //加载类的架构数字 UNIT = intCObject* (PASCAL* m_pfnCreateObject)();CRuntimeClass* m_pBaseClass;//运行类型个体被链接到一张单一的链表上static CRuntimeClass* pFirstClass;  //链表头CRuntimeClass* m_pNextClass;        //通过此指针链接被注册的类};

我们的数据单元中主要储存以下信息:类型的名字,类型的大小,类型的编号(全部被指定为0xFFFF,说实话也不知道这么翻译对不对),一个全局的静态的表头,往往指向末端的子类,一个指向该类型基类的单元的指针,。一个指向下一个类型单元的指针(主要用于结构的链接与遍历查找),还有一个奇奇怪怪的函数指针,其他的以后再说,我们目前只需要关注:类名称,链表的Next指针,以及链表的全局First指针即可。

如果要做到运行时的类型识别,那我们还是需要在每个类的里面塞进我们的这个保存有类型信息的结构,这样在判断某实例类型的时候才可以有据可查,对于这个结构的添加,MFC这里采用了这样一种方式:

#define DECLARE_DYNAMIC(class_name) \public: \static CRuntimeClass class##class_name; \virtual CRuntimeClass* GetRuntimeClass() const;
声明了这样一个宏,其实内容很简单,宏的内容就是声明一个对应类的局部变量和对应一个公有方法,其中局部变量的名字随绑定类型而动态变化,该变量实际保存的就是之前的藏有类型信息的结构,可是光有这样的结构还不行,我们还需要对这样的结构的内容进行填充并且将类型之间全部链接起来,建立起他们本该由的联系

在这里再先看一个有趣的结构:

struct AFX_CLASSINIT{AFX_CLASSINIT(CRuntimeClass* pNewClass);};
这个结构体本身无其他含义,重点在于他的构造函数上,其构造函数内容如下:

AFX_CLASSINIT::AFX_CLASSINIT(CRuntimeClass* pNewClass){pNewClass->m_pNextClass = CRuntimeClass::pFirstClass;CRuntimeClass::pFirstClass = pNewClass;}
可以看到藉由这个结构的构造函数可以完成类型之间的链接工作,看完这些之后让我们看看今天的主菜:

#define _IMPLEMENT_RUNTIMECLASS(class_name , base_class_name,wSchema,pfnNew) \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}; \static AFX_CLASSINIT _init_##class_name(&class_name::class##class_name); \CRuntimeClass* class_name::GetRuntimeClass() const \{ return &class_name::class##class_name ; } \#define IMPLEMENT_DYNAMIC(class_name , base_class_name) \_IMPLEMENT_RUNTIMECLASS(class_name ,base_class_name , 0xFFFF , NULL)
是否看之前就已经产生了恐惧?没错,这样的大段的宏对于像我这种菜鸡来说确实很吓人,不过其实静下来看完之后就很简单了,就是一大段一大段的文本替换,由于类型识别表创建的过程中需要做大量的很类似的操作,且由于一些特殊的问题(变量名)所以没法直接利用抽象完成,所以使用了宏定义来解离了这一部分的冗余(也不知道这样说对否,反正读起来有模板的感觉在里面,可惜模板做不到),这一段宏定义实际的作用其实就是:

//定义一个静态变量保存对应类的名字字段   。。预编译阶段生效
//为对应类的静态 class##class_name 字段初始化赋值
//定义一个静态的AFX_CLASSINIT的变量_init_##class_name使得被创建的时刻调用对应得构造函数,其作用是串接类型的链表
//定义之前类中声明的虚函数 GetRuntimeClass方法 ,返回的对象为类中的结构单元字段    只是一个方法

有没有发现结合以上的这些操作正好就可以完成类型的初始化以及链接工作

通过以下面这段姿势使用这个宏:

IMPLEMENT_DYNAMIC(CCmdTarget, CObject)IMPLEMENT_DYNAMIC(CWinThread, CCmdTarget)IMPLEMENT_DYNAMIC(CWinApp, CWinThread)IMPLEMENT_DYNAMIC(CWnd, CCmdTarget)IMPLEMENT_DYNAMIC(CFrameWnd, CWnd)IMPLEMENT_DYNAMIC(CDocument, CCmdTarget)IMPLEMENT_DYNAMIC(CView, CWnd)
我们就完成了整张类型表的链接工作

是不是很简单?不过在这里我们还要顺便考虑一个问题,就是对于这个型录的表头我们需要特殊处理一下(MFC的所有的类都继承自CObject,这个独特而又极具威慑力的设计被应用于很多地方)

具体代码如下:

static char szCObject[] = "CObject";struct CRuntimeClass CObject::classCObject ={ szCObject , sizeof(CObject) , 0xffff,NULL,NULL,NULL };static AFX_CLASSINIT _init_CObject(&CObject::classCObject);CRuntimeClass* CObject::GetRuntimeClass() const{    return &CObject::classCObject;}class CObject{public:    CObject::CObject(){}    CObject::~CObject(){}    virtual CRuntimeClass* GetRuntimeClass() const;public:    static CRuntimeClass classCObject;};
其实也没有什么东西,就是比标准操作减少了一点多余的操作,这样我们的整张型录就完成了·

在MFC的初代还没有RTTI,MFC只好通过自己来实现这种类型识别现在想起来简直是不容易。

以上代码段均借鉴于侯俊杰先生的《深入浅出MFC》

完整代码如下:

#pragma once//MFC.h//运行时类型识别//宏命令中单#代表表示转义字符串//宏命令中双#代表文本链接#define BOOL int#define TRUE 1#define FALSE 0#define LPCSTR LPSTR   //实际上就只是一个char型的指针typedef char* LPSTR;#define UINT int#define PASCAL _stdcall#include<iostream>using namespace std;class CObject;struct  CRuntimeClass{LPCSTR m_lpszClassName;  // LOCSTR = char*int m_nObjectSize;UINT m_wSchema; //加载类的架构数字 UNIT = intCObject* (PASCAL* m_pfnCreateObject)();  //所有将会被记录的类型的公共父类型CRuntimeClass* m_pBaseClass;//运行类型个体被链接到一张单一的链表上static CRuntimeClass* pFirstClass;  //链表头CRuntimeClass* m_pNextClass;        //通过此指针链接被注册的类};struct AFX_CLASSINIT{AFX_CLASSINIT(CRuntimeClass* pNewClass);};#define RUNTIME_CLASS(class_name) \(&class_name::class##class_name)#define DECLARE_DYNAMIC(class_name) \public: \static CRuntimeClass class##class_name; \virtual CRuntimeClass* GetRuntimeClass() const;//下面这段宏的解释://第一句:定义一个静态变量保存对应类的名字字段   。。预编译阶段生效//第2句-第4句:为对应类的静态 class##class_name 字段初始化赋值//第5句:定义一个静态的AFX_CLASSINIT的变量_init_##class_name使得被创建的时刻调用对应得构造函数,其作用是串接类型的链表//第6-7句:定义之前类中声明的虚函数 GetRuntimeClass方法 ,返回的对象为类中的结构单元字段    只是一个方法#define _IMPLEMENT_RUNTIMECLASS(class_name , base_class_name,wSchema,pfnNew) \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}; \static AFX_CLASSINIT _init_##class_name(&class_name::class##class_name); \CRuntimeClass* class_name::GetRuntimeClass() const \{ return &class_name::class##class_name ; } \#define IMPLEMENT_DYNAMIC(class_name , base_class_name) \_IMPLEMENT_RUNTIMECLASS(class_name ,base_class_name , 0xFFFF , NULL)class CObject{public:CObject::CObject(){}CObject::~CObject(){}virtual CRuntimeClass* GetRuntimeClass() const;public:static CRuntimeClass classCObject;};class CCmdTarget : public CObject{DECLARE_DYNAMIC(CCmdTarget)public:CCmdTarget::CCmdTarget(){}CCmdTarget::~CCmdTarget(){}};class CWinThread :public CCmdTarget{DECLARE_DYNAMIC(CWinThread);public:CWinThread::CWinThread(){}CWinThread::~CWinThread(){}virtual BOOL InitInstance() {return TRUE;}virtual int Run() {return 1;}};class CWnd;class CWinApp :public CWinThread{DECLARE_DYNAMIC(CWinApp)public:CWinApp* m_pCurrentWinApp;CWnd* m_pMainWnd;public:CWinApp::CWinApp() {m_pCurrentWinApp = this;}CWinApp::~CWinApp(){}virtual BOOL InitApplication() {return TRUE;}virtual BOOL InitInstance() {return TRUE;}virtual int Run() {return CWinThread::Run();}};class CDocument : public CCmdTarget{DECLARE_DYNAMIC(CDocument)public:CDocument::CDocument(){}CDocument::~CDocument(){}};class CWnd :public CCmdTarget{DECLARE_DYNAMIC(CWnd)public:CWnd::CWnd() {}CWnd::~CWnd(){}virtual BOOL Create();BOOL CreateEX();virtual BOOL PreCreateWindow();};class CFrameWnd :public CWnd {DECLARE_DYNAMIC(CFrameWnd)public:CFrameWnd::CFrameWnd(){}CFrameWnd::~CFrameWnd(){}BOOL Create();virtual BOOL PreCreateWindow();};class CView :public CWnd{DECLARE_DYNAMIC(CView)public:CView::CView() {}CView::~CView(){}};//全局函数CWinApp* AfxGetApp();

//MY.h#pragma once#include<iostream>#include"MFC.h"using namespace std;class CMyWinApp : public CWinApp{public:CMyWinApp::CMyWinApp() {}CMyWinApp::~CMyWinApp() {}virtual BOOL InitInstance();};class CMyFrameWnd : public CFrameWnd{public:CMyFrameWnd();~CMyFrameWnd(){}};class CMyDoc :public CDocument{public:CMyDoc::CMyDoc(){}CMyDoc::~CMyDoc(){}};class CMyView :public CView{public:CMyView::CMyView(){}CMyView::~CMyView(){}};//全局函数void m_PrintAllClasses();

//MFC.cpp#include"MY.h"extern CMyWinApp theApp;static char szCObject[] = "CObject";struct CRuntimeClass CObject::classCObject ={ szCObject , sizeof(CObject) , 0xffff,NULL,NULL,NULL };static AFX_CLASSINIT _init_CObject(&CObject::classCObject);CRuntimeClass* CRuntimeClass::pFirstClass = NULL;AFX_CLASSINIT::AFX_CLASSINIT(CRuntimeClass* pNewClass){pNewClass->m_pNextClass = CRuntimeClass::pFirstClass;CRuntimeClass::pFirstClass = pNewClass;}CRuntimeClass* CObject::GetRuntimeClass() const{return &CObject::classCObject;}BOOL CWnd::Create(){return TRUE;}BOOL CWnd::CreateEX(){PreCreateWindow();return TRUE;}BOOL CWnd::PreCreateWindow() {return TRUE;}BOOL CFrameWnd::Create() {CreateEX();return TRUE;}BOOL CFrameWnd::PreCreateWindow(){return TRUE;}IMPLEMENT_DYNAMIC(CCmdTarget, CObject)IMPLEMENT_DYNAMIC(CWinThread, CCmdTarget)IMPLEMENT_DYNAMIC(CWinApp, CWinThread)IMPLEMENT_DYNAMIC(CWnd, CCmdTarget)IMPLEMENT_DYNAMIC(CFrameWnd, CWnd)IMPLEMENT_DYNAMIC(CDocument, CCmdTarget)IMPLEMENT_DYNAMIC(CView, CWnd)//全局函数CWinApp* AfxGetApp(){return theApp.m_pCurrentWinApp;}

//MF.cpp#include"MY.h"CMyWinApp theApp;BOOL CMyWinApp::InitInstance(){m_pMainWnd = new CMyFrameWnd;return TRUE;}CMyFrameWnd::CMyFrameWnd() {Create();}void m_PrintAllClasses(){CRuntimeClass* pClass;for (pClass = CRuntimeClass::pFirstClass;pClass != NULL;pClass = pClass->m_pNextClass){cout << pClass->m_lpszClassName << endl;cout << pClass->m_nObjectSize << endl;cout << pClass->m_wSchema << endl;}}void main(){CWinApp* pApp = AfxGetApp();pApp->InitApplication();pApp->InitInstance();pApp->Run();m_PrintAllClasses();system("pause");}


0 0
原创粉丝点击