绪论
前面几篇文章解介绍了MFC的程序启动机制、窗口创建机制以及消息映射机制。今天将介绍MFC另外的两大机制运行时类信息机制和动态创建机制。
运行时类信息机制是动态创建的基础。动态创建机制是在运行时类信息机制的基础上实现的。运行时类信息机制就是知道对象属于那个类,就像我们C++使用的typeid运算子一样。
MFC的动态创建机制就是运行时类信息的结构的基础上动态的创建对象,接下来详细的介绍这两种机制。
运行时类信息机制(RTTI)
利用CObject::IsKindOf函数判断对象是否属于某个类
运行时类信息机制的使用
-
类必须派生自CObject
-
类内必须添加声明 DECLARE_DYNAMIC(theclass)
-
类外必须添加实现宏 IMPLEMENT_DYNAMIC(theclass,baseClass)
当一个类具备上述三个要件后,CObject::IsKindOf函数就可以正确判断对象是否属于某个类
运行时类信息机制的实现原理
MFC是如何通过IsKindOf就可以知道一个对象是否属于一个类呢?
为了进一步研究,我们将创建几个具有继承关系的类,并按照运行时类信息机制的使用声明这几个宏,最后将这几个宏进行展开。
class CAnimal :pub1ic Cobject
{
DECLARE_DYNAMIC(CAnima1) //类内声明
//DECLARE_DYNAMIC(CAnima1)的展开
public:
static const CRuntimeClass classCAnimal;
virtual CRunt imeClass* GetRuntimeClass() const;
}
//类外实现宏
IMPLEMENT_DYNAMIC(CAnimal, Cobject)
// IMPLEMENT_DYNAMIC(CAnimal, Cobject)的展开是IMPLEMENT_RUNTIMECLASS宏
// IMPLEMENT_RUNTIMECLASS(CAnimal, Cobject, 0xFFF, NULL, NULL)
// IMPLEMENT_RUNTIMECLASS(c1ass_name, base_class_name,0xFFFF,NULL,NULL)展开
JAFX_ COMDAT const CRuntimeClass CAnimal: :classCAnimal = {
"CAnimal",
sizeof(class CAnima1),
0xFFFF,
NULL,
// RUNTIME_CLASS(cobject),
//此处将RUNTIMB_CLASS(CObject)展开为_RUNTIMECLASS(class_name)
//再将RUNTIME_CLASS(c1ass_name)宏展开为
((CRuntimeClass*)(&CObject::classC0bject)),
NULL,
NULL
}
CRuntimeClass* CAnimal::GetRuntimeClass() const
{
return RUNTIME_CLASS(CAnimal);
}
将每个宏展开可知
在声明宏中有一个静态变量 static const CRuntimeClass classCAnimal;和一个获取静态变量的虚函数。
在实现宏中对静态变量进行赋值 和 对虚函数进行实现。接下来对CRuntimeClass这个结构进行分析
RuntimeClass数据结构简介
CRuntimeClass中不仅包含属性还包含一些函数,为了简化介绍,只介绍CRuntimeClass的属性
struct CRuntimeClass
{
LPCSTR m_lpszClassName; //类名称
int m_nObjectSize; //类大小
UINT m_wSchema; //类版本(无用)
CObject*(PASCAL* m_pfnCreateObject)(); //动态创建机制使用,这里为NULL
CRuntimeClass* m_pBaseClass; //父类宏展开静态变量地址
CRuntimeclass* m_pNextClass; //不使用 为NULL
const AFX_CLASSINIT* m_pClassInit; //不使用 为NULL
};
一个非常重要的宏RUNTIME_CLASS(theClass)
RUNTIME_CLASS(theClass) ----> &theClass::classtheclass; 获取thecClass这个类的静态变量classtheclass的地址
根据上面数据结构可知每一子类都会保存一个父类的静态变量,这样就会形成如下的根据继承关系形成的链表(类继承关系为CObject->CAnimal->CDog)
如上图所示,通过 RuntimeClass数据结构形成了一个与继承相关的链表。通过该链表可以判断该对象是否属于该类。它的执行过程如下:
运行时类信息机制执行过程
dog.IsKinnOf(RUNTIME_CLASS(CAnimal))
{
CRuntimeClass* pClassThis = GetRuntimeClass(); //调用虚函数,获取头节点
//利用IsDeriveFrom进行链表的遍历,是否能够找到与pClass相同的类,IsDeriveFrom的具体执行如下。
return pClassThis->IsDeriveFrom(pClass);
{
const CRuntimeClass* pClassThis = this; //获取头节点
//进行循环遍历
while (pClass != NULL)
{
if (pClassThis == pClass)//判读与该父类对象是否相同
{
return true;
}
pClassThis = pClassThis->m_pBaseClass
}
return false;
}
}
总结:
执行过程如下
- 利用对象的地址调用宏展开的虚函数GetRuntimeClass()获取本类静态变量的地址(链表头)
- 利用本类静态变量地址(链表头)和目标进行比对
- 如果相同,证明对象属于这个类
- 如果不相同就会获取链表的下一个节点循环进行比对,一直到链表尾部。
动态创建机制
动态创建机制可以在不知道类名的情况下,将类的对象创建出来。但这个对象要有几个特征,接下来具体介绍一下。
动态创建机制的使用
类必须派生自CObject
类内必须添加声明宏 DECLARE_DYNCREATE (theClass)
类外必须添加实现宏 IMPLEMENT_DYCREATE(theClass,baseClass)
但一个类具备上述三个要件后,CRuntimeClass::CreateObject(对象加工厂)函数就可以将类的对象创建出来。
动态创建机制的原理
为了能够及更好的理解动态创建机制的原理,将上面的宏全部展开为
class CDog :public CAnimal
{
//动态创建类内声明宏
DECLARE_DYNCREATE (CDog)
//DECLARE_ DYNCREATE (CDog)展开为
DECLARE_DYNAMIC(CDog)
static Cobject* PASCAL CreateObject();
//DECLARE DYNCREATE中包含了运行时类信息机制的类内声明宏,将DECLARE_ DYNAMIC (CDog) 展开为
public:
static const CRuntimeClass classCDog ;
virtual CRuntimeClass* GetRuntimeC1ass() const;
}
//动态创建机制类外实现宏
IMPLEMENT_DYNCREATE(CDog,CAnima1)
// IMPLEMENT_DYNCREATE(CDog, CAnimal)展开为
CObject* PASCAL CDog::Create0bject()
{
return new CDog;
}
IMPLEMENT_RUNTIMECLASS(CDog,CAnimal, 0xFFFF,CDog::Create0bject, NULL)
//与运行时类信息机制相同该宏内也包含了IMPLEMENT_RUNTIMECLASS
//将IMPLEMENT_RUNTIMECLASS展开为
AFX_COMDAT const CRuntimeC1ass CDog::classCDog = {
"CDog",
sizeof (class CDog),
0xFFF,
CDog::Create0bject,
RUNTIMECLASS(CAnima1),
NULL,
NULL
};
CRuntimeClass* CDog::GetRuntimeC1ass() const
{
return RUNTIMECLASS (CDog);
}
动态创建机制与运行时类信息机制的区别
通过对动态创建机制和运行时类信息机制的宏展开可以得到动态创建机制与运行时类信息机制的区别:
-
多了一个静态函数
CObject* PASCAL CDog::Create0bject()
{
return new CDog;
} -
静态变量的第四个成员不再为NULL,而为新增加的这个静态函数的地址CDog::Create0bject
动态创建机制的执行过程
通过与运行时类信息机制对比,动态创建机制与运行时类信息机制相同都维护这一个与继承关系相对应的链表。由此可知动态创建机制的执行过程如下:
- 利用本类(CDog)的静态变量CRuntimeClass调用其成员函数CreateObject(对象加工厂函数)
- 获取静态变量的第四个成员CreateObject(),并调用
- 在CreateObject();的内部,完成对象的创建,并返回对象的地址。
结语
运行时类信息机制和动态创建机制都是利用维护一个与继承关系相对应的链表。通过此链表来找到对应类的信息,从而获取类的信息与创建对象。MFC利用该机制可以在框架内部动态的创建视图类CView和CDocument,并将这两个类建立关系。运行时类信息机制和动态创建机制在MFC中的应用请观看我的下一篇文章MFC的视图类与文档类及其关系。