在MFC中RTTI 机制的实现时通过两个宏:
DECLARE_DYNAMIC 和
IMPLEMENT_DYNAMICLASS来实现的, 相信大家也都知道宏的主要作用是什么,简化代码的书写,提高代码可读性。把复杂以及难以使用的代码 包装成宏来使用。RTTI全名 run time type information 运行时类型信息,通过其名称可以知道其作用时在运行时来判断类型的信息。
这段代码把链表的节点链接起来 AFX_CLASSINIT的构造函数
我们来大概 的看一下 创建的对象是如何连接起来的当我们又多个类的时候:
我们来看看MFC中是如何实现的:
我首先把宏给还原(不同版本的MFC代码不一样,但是剖析的方法是一样的):
DECLARE_DYNAMIC
#define DECLARE_DYNAMIC(class_name)\
public:\
static CRuntimeClass class##class_name; \
virtual CRuntimeClass* GetRuntimeClass() const;
当我们需要使用RTTI机制是我们需要在类的定义中加入
DECLARE_DYNAMIC而宏展开以后 为我们在类中添加了一个静态变量和一个虚函数。(##的意思是将##前面的内容和##的参数连接起来,比如:class_name 是 myclass class##class_name 就是 classmyclass)。要弄明白这个宏我们就得了解 CRuntimeClass(运行时类)。
我们看一下CRuntimeClass 的信息:
struct CRuntimeClass
{
LPCSTR m_lpszClassName; //类的名字
int m_nObjectSize; //类的大小
UINT m_wSchema; //shema number of the loaded class
CObject* (PASCAL* m_pfnCreateObject)(); //用来动态创建对象的函数指针,这里不做解释
CRuntimeClass* m_pBaseClass;//表示 基类的 CRuntimeClass
static CRuntimeClass* pFirstClass;// 类链表的头指针
CRuntimeClass* m_pNextClass;// 用来构建和访问类链表
};
这里给出的是简化版的CRuntimeClass,一些我们需要的内容,具体的内容可以再MFC中查看对应的头文件,也可以用MSDN查看。
很明显的是 这个结构体保存了类的名称,大小,还有两个指针,通过两个指针的名字我们很容易想到一个链表。可以猜测,RTTI 不过也是通过一个链表来保存类的信息。
当我们在类中加入这个宏的时候类得结构变成下图:
每一个类中都会又这两个成员,所以把所有的类连城一个链表就容易了。
接下来我们就看看这两个成员的初始化过程,没错就是 IMPLEMENT_DYNAMICLASS宏
再看这个宏之前我们还得看一下其他的几个宏:
当我们把宏替换以后就可以看到完整的代码:
#define RUNTIME_CLASS(class_name)\
(&class_name::class##class_name)
struct AFX_CLASSINIT
{
AFX_CLASSINIT(CRuntimeClass* 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; } \
为什么要这样定义 IMPLEMENT_DYNAMICLASS 宏因为之后还有动态创建对象也需要使用 _IMPLEMENT_RUNTIMECLASS 这样让代码更灵活
#define IMPLEMENT_DYNAMICLASS(class_name, base_class_name)\
_IMPLEMENT_RUNTIMECLASS(class_name, base_class_name, 0xffff, NULL)
IMPLEMENT_DYNAMICLASS(CCmdTarget, CObject)
//可以看一下替换后的结果
/*
//我们暂时先不管0xffff, NULL 这两个参数
#define IMPLEMENT_DYNAMICLASS(CCmdTarget, CObject)\
_IMPLEMENT_RUNTIMECLASS(CCmdTarget, CObject, 0xffff, NULL)
#define _IMPLEMENT_RUNTIMECLASS(CCmdTarget, CObject, wSchema, pfnNew)\
static char _lpszCCmdTarget[] = "CCmdTarget"; \为类创建一个字符串名称
CRuntimeClass CCmdTarget::classCCmdTarget = { \初始化类的静态成员变量,CRuntimeClass ,类的静态成员变量必须在使用前初始化
_lpszCCmdTarget, sizeof(CCmdTarget), wSchema, pfnNew, \//这里对照结构体一一赋值
RUNTIME_CLASS(CObject), NULL }; \
static AFX_CLASSINIT _init_CCmdTarget(&class_name::classCCmdTarget); \
CRuntimeClass* CCmdTarget::GetRuntimeClass() const \
{ return &class_name::classCCmdTarget; } \
*/
这段代码把链表的节点链接起来 AFX_CLASSINIT的构造函数
AFX_CLASSINIT::AFX_CLASSINIT(CRuntimeClass* pNewClass)
{
pNewClass->m_pNextClass = CRuntimeClass::pFirstClass;
CRuntimeClass::pFirstClass = pNewClass;
}
我们来大概 的看一下 创建的对象是如何连接起来的当我们又多个类的时候:
所以我们还应该对第一个创建的类做一下特殊处理,应为它没有基类也没有next。一下是完整的代码:来自<<深入浅出MFC>>
#pragma once
#include<iostream>
using namespace std;
class CObject;
#define BOOL int
#define TRUE 1
#define FLASE 0
#define LPCSTR const char*
#define LPSTR char*s
#define UINT int
#define PASCAL __stdcall
struct CRuntimeClass
{
LPCSTR m_lpszClassName; //类的名字
int m_nObjectSize; //类的大小
UINT m_wSchema; //shema number of the loaded class
CObject* (PASCAL* m_pfnCreateObject)(); //NULL=> abstract class,一个指向非空的函数指针
CRuntimeClass* m_pBaseClass;//表示 基类的 CRuntimeClass
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;
//实现定义的宏, 宏中使用一个 # 表示 用引号括起后面的内容 "class_name", 两个引号表示连接
#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_DYNAMICLASS(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;
BOOL IsKindOf(const CRuntimeClass* pClass) const;
public:
static CRuntimeClass classCObject;
};
class CCmdTarget :public CObject
{
DECLARE_DYNAMIC(CCmdTarget)
/*
#define DECLARE_DYNAMIC(CCmdTarget)\
public:\
static CRuntimeClass class##CCmdTarget; \
virtual CRuntimeClass* GetRuntimeClass() const;
*/
public:
CCmdTarget::CCmdTarget(){}
CCmdTarget::~CCmdTarget(){}
};
class CWinThread : public CCmdTarget
{
DECLARE_DYNAMIC(CWinThread)
public:
virtual bool InitInstance()
{
cout << "CWinThread:InitInstance \n";
return true;
}
virtual int Run()
{
cout << "CWinThread:Run \n";
return 1;
}
};
//先声音 CWnd类 因为在 CWinApp中需要用到,但是却没有 实际定义 CWnd这个类
class CWnd;
class CWinApp : public CWinThread
{
DECLARE_DYNAMIC(CWinApp)
public:
CWinApp* m_pCurrentWinApp;
CWnd* m_pMainWnd;
public:
CWinApp::CWinApp()
{
m_pCurrentWinApp = this;
}
virtual bool InitInstance()
{
cout << "CWinApp:InitInstance \n";
return true;
}
virtual int Run()
{
cout << "CWinApp:Run \n";
return CWinThread::Run();
}
virtual bool InitApplication()
{
cout << "CWinApp:InitApplication \n";
return true;
}
};
class CDocument : public CCmdTarget
{
DECLARE_DYNAMIC(CDocument)
public:
};
class CWnd : public CCmdTarget
{
DECLARE_DYNAMIC(CWnd)
public:
virtual bool Create();
bool CreateEx();
virtual bool PreCreateWindow();
};
class CView : public CWnd
{
DECLARE_DYNAMIC(CView)
public:
};
class CFrameWnd : public CWnd
{
DECLARE_DYNAMIC(CFrameWnd)
public:
bool Create();
virtual bool PreCreateWindow();
};
CWinApp* AfxGetApp();
#include "MFC.h"
static char lpszObject[] = "CObject";
//作为链表的尾节点。CObject 有一些特殊,其没有基类
struct CRuntimeClass CObject::classCObject = {
lpszObject, sizeof(CObject), 0xffff, NULL, NULL, NULL
};
static AFX_CLASSINIT _initCObject(&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 CObject::IsKindOf(const CRuntimeClass* pClass) const
{
CRuntimeClass* pClassThis = GetRuntimeClass();
while (pClassThis != NULL)
{
if (pClassThis == pClass)
return TRUE;
pClassThis = pClassThis->m_pBaseClass;
}
return false;
}
IMPLEMENT_DYNAMICLASS(CCmdTarget, CObject)
//可以看一下替换后的结果
/*
//我们暂时先不管0xffff, NULL 这两个参数
#define IMPLEMENT_DYNAMICLASS(CCmdTarget, CObject)\
_IMPLEMENT_RUNTIMECLASS(CCmdTarget, CObject, 0xffff, NULL)
#define _IMPLEMENT_RUNTIMECLASS(CCmdTarget, CObject, wSchema, pfnNew)\
static char _lpszCCmdTarget[] = "CCmdTarget"; \为类创建一个字符串名称
CRuntimeClass CCmdTarget::classCCmdTarget = { \初始化类的静态成员变量,CRuntimeClass ,类的静态成员变量必须在使用前初始化
_lpszCCmdTarget, sizeof(CCmdTarget), wSchema, pfnNew, \//这里对照结构体一一赋值
RUNTIME_CLASS(CObject), NULL }; \
static AFX_CLASSINIT _init_CCmdTarget(&class_name::classCCmdTarget); \
CRuntimeClass* CCmdTarget::GetRuntimeClass() const \
{ return &class_name::classCCmdTarget; } \
*/
IMPLEMENT_DYNAMICLASS(CWinThread, CCmdTarget)
IMPLEMENT_DYNAMICLASS(CWinApp, CWinThread)
IMPLEMENT_DYNAMICLASS(CWnd, CCmdTarget)
IMPLEMENT_DYNCREATE(CFrameWnd, CWnd)
IMPLEMENT_DYNAMICLASS(CDocument, CCmdTarget)
IMPLEMENT_DYNCREATE(CView, CWnd)
bool CWnd::Create()
{
<span style="white-space:pre"> </span>return true;
}
bool CWnd::CreateEx()
{
<span style="white-space:pre"> </span>//由于PreCrete是虚函数,所以PreCreateWindow属于那个类根据this指针决定
<span style="white-space:pre"> </span>PreCreateWindow();
<span style="white-space:pre"> </span>return true;
}
bool CWnd::PreCreateWindow()
{
<span style="white-space:pre"> </span>return true;
}
bool CFrameWnd::Create()
{
<span style="white-space:pre"> </span>//必然是基类的CraeteEx 因为CFrameWnd没有重写
<span style="white-space:pre"> </span>CreateEx();
<span style="white-space:pre"> </span>return true;
}
bool CFrameWnd::PreCreateWindow()
{
<span style="white-space:pre"> </span>return true;
}
void PrintAllClasses()
{
CRuntimeClass* pClass;
for (pClass = CRuntimeClass::pFirstClass; pClass != NULL; pClass = pClass->m_pNextClass)
{
cout << "ClassName: "<<pClass->m_lpszClassName << endl;
cout << "ClassSize: " << pClass->m_nObjectSize << endl;
cout << "ClassSchema: " << pClass->m_wSchema << endl;
cout << endl;
}
}
void main()
{
PrintAllClasses();
CCmdTarget *pCmd = new CCmdTarget();
CWinApp *pApp = new CWinApp();
<span style="white-space:pre"> </span>//IsKindOf只要在基类中定义且子类不重写,则子类都调用基类的IsKindOf
cout << pCmd->IsKindOf(RUNTIME_CLASS(CObject))<<endl;
cout << pApp->IsKindOf(RUNTIME_CLASS(CCmdTarget)) << endl;
cout << pApp->IsKindOf(RUNTIME_CLASS(CObject)) << endl;
cout << pApp->IsKindOf(RUNTIME_CLASS(CWnd)) << endl;
system("pause");
}
程序运行结果:
学习笔记,不对的地方请指教。