1 CRuntimeClass和类别型录网
首先,CRuntimeClass结构和类别型录网是实现RTTI和动态创建这两个技术的基础。
因为早期技术比较单一,没有typeid运算符和template等概念,于是我们为每个类设计了一个对应的CRuntimeClass结构来储存该类的相关信息,将类库中所有类的 CRuntimeClass结构放入一个单向链表中,就构成了一个类别型录网,链表向上遍历类层次,直到表尾CObject::classCObject。
1)CRuntimeClass结构:
{
// Attributes
LPCSTR m_lpszClassName;//类名
int m_nObjectSize;//对象大小
UINT m_wSchema; // schema number of the loaded class
CObject* (PASCAL* m_pfnCreateObject)(); // NULL => abstract class
CRuntimeClass* m_pBaseClass;//指向基类
CObject* CreateObject();//
static CRuntimeClass* PASCAL Load();
// CRuntimeClass objects linked together in simple list
static CRuntimeClass* pFirstClass; // start of class list指向表头
CRuntimeClass* m_pNextClass; // linked list of registered classes指向链表中的下一个类
};
加红色的三个变量都与动态创建有关,在下面说明。
2)类别型录网:
2 DECLARE_DYNAMIC和IMPLEMENT_DYNAMIC宏
我们用这两个宏,去在MFC中实现CRuntimeClass的声明和类别型录网的构成。
1)#define DECLARE_DYNAMIC(class_name)
public:
static CRuntimeClass class##class_name;
virtual CRuntimeClass* GetRuntimeClass() const;
注:DECLARE_DYNAMIC宏声明了一个与Cxxx类相对应的CRuntimeClass结构classCxxx。
2)#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) \
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; } \
其中,RUNTIME_CLASS宏:
#define RUNTIME_CLASS(class_name) \
(&class_name::class##class_name)
和AFX_CLASSINIT结构:
struct AFX_CLASSINIT
{ AFX_CLASSINIT(CRuntimeClass* pNewClass); };
而AFX_CLASSINIT::AFX_CLASSINIT( CRuntimeClass* pNewClass)
{
pNewClass->m_pNextClass = CRuntimeClass::pFirstClass;
CRuntimeClass::pFirstClass = pNewClass ;
}
注:IMPLEMENT_DYNAMIC宏,初始化classCxxx结构,并实现了链表。
3 RTTI
有了类别型录网,现在可以说说类型识别技术了。
例:CView* pView = new CView;
pView->IsKindOf(RUNTIME_CLASS(CWinApp));
RUNTIME_CLASS(CWinApp)返回与CWinApp对应的CRuntimeClass结构指针
类型识别其实是靠m_pBaseClass的向上索引,两个类的CRuntimeClass指针的比较。
4 动态创建
1)DECLARE_DYNCREATE和IMPLEMENT_DYNCREATE宏
DECLARE_DYNCREATE:
#define DECLARE_DYNCREATE(class_name)
DECLARE_DYNAMIC(class_name) \
static CObject* PASCAL CreateObject();
IMPLEMENT_DYNCREATE宏:
#define IMPLEMENT_DYNCREATE(class_name, base_class_name) \
CObject* PASCAL class_name::CreateObject() \
{ return new class_name; } \
_IMPLEMENT_RUNTIMECLASS(class_name, base_class_name, 0xFFFF, \
class_name::CreateObject)
注:这两个宏用于动态创建,且可以看到_DYNCREATE宏包含了_DYNAMIC宏。
2)动态创建
在头文件中加入DECLARE_DYNCREATE,在实现文件中加入IMPLEMENT_DYNCREATE,我们就可以进行动态创建类了。
我们这里先把main中的动态创建语句写出来,反向思考看看动态创建的执行过程。
void main()
{
...
CRuntimeClass* pClassRef;
CObject* pOb;
while(1)
{
if((pClassRef = CRuntimeClass::Load()) == NULL)
break;
pOb = pClassRef -> CreateObject();
if(pOb != NULL)
pOb->sayhello();
}
}
首先执行到CRuntimeClass::Load(),看看load函数定义我们就会知道,它加载了一个类名,然后遍历类别型录网,当找到这个类对应的CRuntimeClass结构时,返回该结构的指针,否则返回null。然后到CreateObject(),再看看这个函数定义,它先判断m_pfnCreateObject指针是否为空,如果不为空,我们就调用该指针指向的函数classname::CreateObject()进行对象的创建,并返回对象指针完成动态创建。
注:值得一提的是m_pfnCreateObject指针,它是一个函数指针,保存着相应类的CreateObject()函数地址,而类的CreateObject()函数是在DECLARE_DYNCREATE宏中声明的(从上面可以看到),而且该函数其实是一个静态回调函数。所以,如果我们没有在类中加入DECLARE_DYNCREATE宏,m_pfnCreateObject指针为空,也就无法动态创建该类对象。
终于把这两个技术说了一遍,技术很强大,我希望能用浅显易懂的方式说给诸位,不当之处请见谅~