MFC运行时类信息机制及动态创建机制

本文详细阐述了MFC中的运行时类信息机制,如何通过CObject::IsKindOf判断对象类型,并介绍了 DECLARE_DYNAMIC 和 IMPLEMENT_DYNAMIC宏的使用。随后深入探讨了动态创建机制,包括如何在不知道类名时创建对象,以及与运行时类信息机制的对比。

绪论

前面几篇文章解介绍了MFC的程序启动机制窗口创建机制以及消息映射机制。今天将介绍MFC另外的两大机制运行时类信息机制和动态创建机制。
运行时类信息机制是动态创建的基础。动态创建机制是在运行时类信息机制的基础上实现的。运行时类信息机制就是知道对象属于那个类,就像我们C++使用的typeid运算子一样。
MFC的动态创建机制就是运行时类信息的结构的基础上动态的创建对象,接下来详细的介绍这两种机制。

运行时类信息机制(RTTI)

利用CObject::IsKindOf函数判断对象是否属于某个类

运行时类信息机制的使用

  1. 类必须派生自CObject

  2. 类内必须添加声明 DECLARE_DYNAMIC(theclass)

  3. 类外必须添加实现宏 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)

CDog
CRuntimeClass classCDog;
GetRuntimeClass()
CAnimal
CRuntimeClass classCAnimal;
GetRuntimeClass()
CObject
CRuntimeClass classObject;
GetRuntimeClass()
NULL
CRuntimeDog
.....
CAnimal::classAnimal
......
CRuntimeAnimal
.....
CAnimal::classCObject
......
CRuntimeObject
.....
CAnimal::classCObject
......

如上图所示,通过 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;
                       }
}

总结:
执行过程如下

  1. 利用对象的地址调用宏展开的虚函数GetRuntimeClass()获取本类静态变量的地址(链表头)
  2. 利用本类静态变量地址(链表头)和目标进行比对
  3. 如果相同,证明对象属于这个类
  4. 如果不相同就会获取链表的下一个节点循环进行比对,一直到链表尾部。

动态创建机制

动态创建机制可以在不知道类名的情况下,将类的对象创建出来。但这个对象要有几个特征,接下来具体介绍一下。

动态创建机制的使用

类必须派生自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);
}

动态创建机制与运行时类信息机制的区别

通过对动态创建机制和运行时类信息机制的宏展开可以得到动态创建机制与运行时类信息机制的区别:

  1. 多了一个静态函数

    CObject* PASCAL CDog::Create0bject()
    {
    return new CDog;
    }

  2. 静态变量的第四个成员不再为NULL,而为新增加的这个静态函数的地址CDog::Create0bject

动态创建机制的执行过程

通过与运行时类信息机制对比,动态创建机制与运行时类信息机制相同都维护这一个与继承关系相对应的链表。由此可知动态创建机制的执行过程如下:

  1. 利用本类(CDog)的静态变量CRuntimeClass调用其成员函数CreateObject(对象加工厂函数)
  2. 获取静态变量的第四个成员CreateObject(),并调用
  3. 在CreateObject();的内部,完成对象的创建,并返回对象的地址。

结语

运行时类信息机制和动态创建机制都是利用维护一个与继承关系相对应的链表。通过此链表来找到对应类的信息,从而获取类的信息与创建对象。MFC利用该机制可以在框架内部动态的创建视图类CView和CDocument,并将这两个类建立关系。运行时类信息机制和动态创建机制在MFC中的应用请观看我的下一篇文章MFC的视图类与文档类及其关系。

静态链表是一种使用数组实现的链表数据结构,它通过数组中的元素来存储链表的结点,并使用一个指针来表示下一个结点的位置。在静态链表中,每个元素都包含两个部分:数据部分和指针部分。 在创建静态链表,需要定义一个数组来存储链表的结点,同需要定义一个变量来表示链表的头结点。头结点通常不包含有效数据,其指针部分指向链表的第一个结点。同,数组中的每个元素都需要包含一个指针部分,用于指向下一个结点的位置。 下面是一个简单的静态链表创建的示例代码: ```c #include <stdio.h> // 定义链表结点 typedef struct Node { int data; // 数据部分 int next; // 指针部分 } Node; // 静态链表创建函数 int createList(Node list[], int n) { int head = 0; // 头结点位置 int i; // 初始化链表 for (i = 0; i < n; i++) { list[i].next = -1; } // 读入链表数据 for (i = 0; i < n; i++) { scanf("%d", &list[i].data); } // 链表头结点指向第一个结点 if (n > 0) { head = 0; list[head].next = 1; } // 设置结点的指针部分 for (i = 1; i < n; i++) { list[i].next = i + 1; } // 最后一个结点指向空 if (n > 0) { list[n-1].next = -1; } return head; } int main() { Node list[100]; int n, head, i; // 读入链表长度 scanf("%d", &n); // 创建链表 head = createList(list, n); // 输出链表内容 i = head; while (i != -1) { printf("%d ", list[i].data); i = list[i].next; } printf("\n"); return 0; } ``` 在上面的代码中,我们定义了一个 `Node` 结构体来表示链表的结点,其中包含数据部分和指针部分。我们还定义了一个 `createList` 函数来创建静态链表,该函数接受一个数组和一个整数作为参数,分别表示链表的存储空间和链表的长度。在函数中,我们首先初始化链表,然后读入链表数据,并设置链表结点的指针部分。最后,我们返回头结点的位置。 在 `main` 函数中,我们首先读入链表的长度,然后调用 `createList` 函数创建链表。最后,我们输出链表的内容。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值