RTTI之建构类别型录表 学习总结

来源:互联网 发布:知到app官方下载 编辑:程序博客网 时间:2024/05/18 04:54

RTTI之建构类别型录表 学习总结

什么是RTTIRuntime Type Identification ,运行时类型识别。也就是程序在执行过程中,每当遇到一个对象(实例),如何来判断它的类型。即看它到底是属于哪个类的。

 

 

这同我们日常生活中的一些行为十分相像。由于我们现实生活中的事物都可以用对象来表述,所以对于对象的分类就显得重要。我们平时到商店去购物,都会先看每一层的商品分布图,正是这种分门别类的管理让我们的行动不至于混乱。而像这种商品分布图的创建,其中的一个重要依据就是商品的类型。类似的例子还有许多,不再赘述。我们在这里谈到的RTTI,就像是我们从类型出发找商品的一个逆过程,好比手里拿到了一根臂力棒,想知道它所属的规格(重量级),需要到相应的规格列表中查询一样。RTTI机制的实现,体现了C++语言的面向对象这一思想。

 

对于RTTI的设计,就是用编一份“类别型录”的方法,把程序中用到的所有类编入这张表中,以待将来的比较。这张“类别型录网”将采用链表的方式构建起来,而链表中的每个元素,即是我们要讨论的主角,以CRuntimeClass为类型的结构体。在此列出它的属性:

 

struct CRuntimeClass

{

LPCSTR m_lpszClassName;

int m_nObjectSize;

UINT m_wSchema;

CObject* (PASCAL* m_pfnCreateObject)();

CRuntimeClass* m_pBaseClass;

 

//CRuntimeClass objects linked together in simple list

static CRuntimeClass* pFirstClass;

CRuntimeClass* m_pNextClass;

};

 

 

在进入细节之前,先让我们构想一下成型后的样子。类别型录网的每个元素是CRuntimeClass类型的对象,这每个对象就完全代表了它所属的类。因此类的个数,就是型录表中元素的个数。当我们遇到一个对象,就去检索这张表,以比较的方式找出它的类型。

 

CRuntimeClass类型的对象是类的静态(static)成员变量,所以每个类仅存有唯一的一份。在这个对象中,记录了类的名称、大小等基本信息,而我们要把注意力集中在它的m_pFirstClassm_pNextClass这两个CRuntimeClass类型指针上,因为它们对于型录表的构建和查询的进行都起到了至关重要的作用。

 

我们采用一个宏DECLARE_DYNAMIC来把CRuntimeClass类型的对象塞到类中,并用另一个宏IMPLEMENT_DYNAMIC来给予赋值(关于这个两个宏的详细请参看侯捷老师的《深入浅出MFC》一书,其中有详细解释,因在此主要讨论心得,故不详细说明)。注意这两个宏的名字,declareimplement,粗心大意的我看了几遍才发现它们的联系,实在惭愧。DECLARE_DYNAMIC的作用,是把CRuntimeClass的对象放到类之中;IMPLEMENT_DYNAMIC的作用,是负责初始化CRuntimeClass的对象,并完成它们的链接工作。在此,我们主要讨论IMPLEMENT_DYNAMIC

 

当初看到这个宏时,真是被吓了一跳。只见过#define TURE 1 之类的我真有点受不了。对于这些宏的学习,我采用了划分成几个部分,定义与实现相结合,然后逐个击破的方法。其中宏的参数是理解的关键点。回到具体上来,对于IMPLEMENT_DYNAMIC中指定初值的工作,只要看懂了宏也就十分清楚了。当时自己理解的难点主要是在链表(型录表)的链接上。我们把着眼点的范围缩小,紧盯在链表的链接部分上。

 

Static AFX_CLASSINIT _init_##class_name(&class_name::class##class_name);  

其中AFX_CLASSINIT是一个结构体,定义如下:

 

Struct AFX_CLASSINIT

{ AFX_CLASSINIT(CRuntimeClass* pNewClass); };

 

这里的函数是结构体的构造函数(struct也是class,区别在于属性的默认访问权限不同),定义如下:

AFX_CLASSINIT::AFX_CLASSINIT(CRuntimeClass* pNewClass)

{

pNewClass->m_pNextClass = CRuntimeClass::pFirstClass;

CRuntimeClass::pFirstClass = pNewClass;

}

 

这个构造函数,就是“折磨”我许久的本源。其实说到心得,上边所说的都不要,从这里开始也可以。

 

注意它的形参( CRuntimeClass* pNewClass ),pNewClass--新类的指针(字面意思)。这里新类的含义,就是我们即将要加入型录表中的那个类。看一下实例代码:

 

Static AFX_CLASSINIT _init_CView(&CView::classCView);

 

 

其中,CView是我们即将要加入型录表中的新类。然后对于类的全权代表,CRuntimeClass类型的对象classCView,做了取地址操作,把它的地址传了进来。接着,

 

pNewClass->m_pNextClass = CRuntimeClass::pFirstClass;

 

 

当时看到CRuntimeClass::pFirstClass就犯嘀咕了,它代表什么呢?继续向下,

 

CRuntimeClass::pFirstClass = pNewClass;

 

 

我们为它赋的值是新类(当前待插入类)的CRuntimeClass的对象地址。所以,pFirstClass指向了当前传入的对象,即链表中的最后一个元素。说到这里,有两点需要注意:

1、pFirstClassCRuntimeCLass的静态(static)成员,所以CRuntimeClass对象中并无此属性,需用类名CRuntimeClass::来调用。

2、对于第一个插入的类,pFirstClass = NULL ,这在稍后链表头的处理中会说到。

允许我补充一点:pFirstClass在整个链接过程中扮演了最最重要的,相当于那根“线”的角色。

综上,链接过程是这样进行的:第一个类的代表(CRuntimeClass的对象)来到,pFirstClass指向它,代表pNextClass为空(注意构造中的语句);2号代表来到,因为pFirstClass中已保存了1号代表的地址,故2号代表的pNextClass指针直接指向1号代表,然后pFirstClass再指向2号代表;3号代表来到,因为pFirstClass中已保存了2号代表的地址,故3号代表的pNextClass指针直接指向2号代表,pFirstClass移至3号。依此进行。这张型录表建构好后,能体现出由下到上的类间系。这种由子到父的顺序,使得查找工作能够从小范围开始,体现了继承的思想。

 

最后来说说链表的头,即类的根源CObject的处理。这里主要就是pFirstClass作为CRuntimeClass的静态成员要进行初始化:

CRuntiamClass* CRuntimeClass::pFirstClass = NULL;

这步关键操作关系到第一个插入的类。总的来说,这个链接过程就是单独处理好头部之后(第一个类),剩下的类按节奏依次插入链表。

 

 

自己是刚开始写学习总结,不足之处请大家多提建设性意见。如果能对各位有所帮助实是我的荣幸。

鼓励交流,只求进步。

 

原创粉丝点击