MFC中的RTTI(Runtime Type Identification, 运行时类型识别)详解(参考《深入浅出MFC》)

在MFC中的RTTI的实现,主要是利用一个名为CRuntimeClass的结构来链接各个“有关系的类”的信息来实现的。简单来说,就是在需要用到RTTI技术的类内建立CRuntimeClass的静态变量,来储存该类的相关信息(包括类名、基类的CRuntimeClass结构的指针、让“有关系的类”的信息形成链表的next指针、以及链表的首指针(静态)等信息),这里的“有关系”指的是从同一个基类继承下来的所有的类之间的关系。这些信息(CRuntimeClass)节点的链接过程,简化如下:

(注意: 某个类 (如,CObject) 的CRuntimeClass的变量名就是class接上其类名称 (如:classCObejct) )

1.建立使用 RTTI 技术的基类时,新建信息链表。

                                        
                                                                                    图1

新建一个需要用到RTTI技术的基类的时候,需要我们创建一个类似图1的CRuntimeClass结构,实际上是初始化了管理所有和该类“有关系”的类的信息的链表,其中,CRuntimeClass的静态变量pFirstClass就是指向该信息链表的首节点的。

2.建立子类时,将子类信息(子类的CRuntimeClass结构)关联到信息链表。(CCmdTarget 继承自 CObject)

图2

将子类的信息也加入到链表中,这时候pFirstClass指针将会指向新的信息。模拟代码如下,

CCmdTarget::classCCmdTarget.next = CRuntimeClass::pFirstClass;
CRuntimeClass::pFirstClass = pCCmdTarget::classCCmdTarget;

3.后面新建子类的情况类似,直接上图。

a) CWinThread继承自CCmdTarget,将其信息节点(classCWinThread)加入链表中。

图3

b)  CWnd继承自CCmdTarget,将其信息节点(CWnd::classCWnd)加入信息链表中。

图4

正是由于存在有这样的链表将“有关系的类”的信息(CRuntimeClass结构)关联起来,因此,我们能够很轻松地实现RTTI的相关功能。

         RTTI提供了以下两个非常有用的功能:

(1)查询指针和引用所指的实际类型。

(2)将基类类型的指针或引用安全地转换为派生类型的指针或引用。

知道了在宏观上的控制,下面我们来仔细解刨一下,MFC在细节处是怎么落实RTTI技术的。

首先,我们来看看每一个信息节点是怎么定义的。

前面提到了,MFC中每一个使用RTTI的类都会创建一个CRuntimeClass的对象来储存对象的信息,“有点关系的类”之间通过这个结构形成的链表关联起来。在侯捷(侯俊杰)先生的《深入浅出MFC(第二版)》中是这样仿真该结构(CRuntimeClass)的。

struct CRuntimeClass
{
      LPCSTR m_lpszClassName; //包含该CRuntimeClass对象的类的名称。
      int m_nObjectSize; //m_lpszClassName占用的字节数
      UINT m_wSchema;  //分类编号
      CObject* (PASCAL* m_pfnCreateObject)(); //NULL => abstract class,存包含该结构对象的类的构造函数的指针
      CRuntimeClass* m_pBaseClass; //包含该结构对象的类的基类的CRuntimeClass对象指针

      //下面是构成链表所需要的变量
      static CRuntimeClass* pFirstClass; //链表的首节点
      CRuntimeClass* m_pNextClass; //该节点的下一个节点。
}

我们知道,在MFC中要用到RTTI时,总是会使用DECLARE_DYNAMIC/IMPLEMENT_DYNAMIC宏,正是这一对宏,链接“有关系的类”的链表。既然说是“一对”,当然是要成对使用的了,一个用于声明,一个用于定义,你挑柴来我织布,夫妻双双把家还。 = =!

在具体讨论源代码前,先要知道,宏定义中用#把宏参数变为一个字符串,用##把两个宏参数贴合在一起。

如,

#include <iostream>
using namespace std;
#define TEST(X) #X
int main()
{
	cout << TEST(TEST) << endl;
}

输出:

图5

再如,

#include <iostream>

using namespace std;
#define TEST(X, Y)  X##Y
int main()
{
	cout << TEST("X#", "#Y") << endl;
}

输出:

图6

好了,有了上面一系列的讨论,现在我们来专专心心分析一下MFC中的RTTI究竟是怎么实现滴。 +_-!

首先是声明,声明的宏DECLARE_DYNAMIC的定义如下:

#define  DECLARE_DYNAMIC(class_name) public: 	static CRuntimeClass class##classname; 	virtual CRuntimeClass* GetRuntimeClass() const;

前面提到,要用到RTTI必须要创建一个静态的CRuntimeClass结构对象,从这个声明的宏,我们可以知道,定义了一个名为class+类名的CRuntimeClass对象,以及回去该对象地址的虚函数。之所以为虚函数,是因为子类和父类返回的CRuntimeClass不一样。

其次就是实现的宏IMPLEMENT_DYNAMIC,这个宏稍微有一点乱,烦请认真地阅读,实在看不懂的话,也没关系,代码后面我有详细地解读。嘿嘿……闲话少说,上代码。

#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; }
#define RUNTIME_CLASS(class_name)  (&class_name::class##class_name)

怎么样?被吓着了没?如果你说No的话,那就推荐你直接去看MFC这一段的源码。

前两行代码,相当于偷梁换柱,这个没什么说的,看得懂宏定义的,都懂!

_IMPLEMENT_RUNTIMECLASS宏,实际上是完成了对DECLARE_DYNAMIC宏声明的CRuntimeClass变量class##class_name的初始化,以及对GetRuntimeClass()函数的定义。下面是截取的部分代码。

static char _lpsz##class_name[] = #class_name;      //获取包含该CRuntimeClass的类的名称
	CRuntimeClass class_name::class##class_name = {\    //初始化CRuntimeClass的参数
	_lpsz##class_name, sizeof(class_name), wSchema, pfnNew, \
	RUNTIME_CLASS(base_class_name), NULL }; \  

在_IMPLEMENT_RUNTIMECLASS宏中用到了AFX_CLASSINIT 结构来对CRuntimeClass进行初始化,使之链接上存有类的信息链表。AFX_CLASSINIT 它的构造函数实现对CRuntimeClass对象class##class_name进行操作。

struct AFX_CLASSINIT
{
	AFX_CLASSINIT(CRuntimeClass* pNewClass)
	{
		pNewClass->m_pNextClass = CRuntimeClass::pFirstClass;
		CRuntimeClass::pFirstClass = pNewClass;
	}
};

虽然可能还没大讲清楚,但是,大概就是这个意思了。还是上个例子吧。

在头文件中,我们用DECLARE_DYNAMIC进行声明。

// In *.h file.
class CWnd : public CCmdTarget
{
public:
	CWnd(){}
	~CWnd(){}

	DECLARE_DYNAMIC(CWnd)
/* 上面的DECLARE_DYNAMIC(CWnd),展开就成为:
public:
	static CRuntimeClass classCWnd; //声明静态变量
	virtual CRuntimeClass* GetRuntimeClass() const;
*/
};

在源文件中,我们用IMPLEMENT_DYNAMIC进行实现。

// In *.cpp file
IMPLEMENT_DYNAMIC(Cwnd, CCmdTarget)
/*IMPLEMENT_DYNAMIC(Cwnd, CCmdTarget),展开就成为:
static char _lpszCWnd[] = "CWnd";
//定义并初始化静态变量classCWnd
CRuntimeClass classCWnd = { _lpszCWnd, sizeof(CWnd), 0xFFFF, NULL,
	                        &CCmdTarget::classCCmdTarget, NULL }
//定义并初始化静态变量_init_CWnd,实际上是在调用其构造函数,将classCWnd连接到链表。
static AFX_CLASSINIT _init_CWnd(&CWnd::classCWnd);
//获得CRuntimeClass对象classCWnd的指针
CRuntimeClass* CWnd::GetRuntimeClass() const
{
	return &CWnd::classCWnd;
}
*/

好了,相信坚持看到这里的小伙伴已经大概了解MFC关于RTTI的最重要的两个宏了吧?关于MFC的RTTI其实还有很多东西可以讨论的,要是有时间的话,我会继续更新相关的内容。\(^o^)/~,好吧,最后来一句:

【参考文献】

1.侯捷著《深入浅出MFC(第二版)》

2. MSDN相关内容

时间: 2024-08-23 23:10:09

MFC中的RTTI(Runtime Type Identification, 运行时类型识别)详解(参考《深入浅出MFC》)的相关文章

moon RTTI --running-time type identification运行时类型确定

JVM 三种预定义类型类加载器 1)启动类(Bootstrap)加载器  :<Java_Runtime_Home>/lib下面的核心类库或-Xbootclasspath选项指定的jar包加载到内存中 Bootstrap /?bu:tstræp/引导程序; 解靴带; 靴袢; 自益; 2)拓展(Extension)类加载器:由Sun的ExtClassLoader(sun.misc.Launcher$ExtClassLoader)实现的.它负责将< Java_Runtime_Home >

C++ - RTTI(RunTime Type Information)执行时类型信息 具体解释

RTTI(RunTime Type Information)执行时类型信息 具体解释 本文地址: http://blog.csdn.net/caroline_wendy/article/details/24369987 RTTI, RunTime Type Information, 执行时类型信息, 是多态的主要组成部分, 通过执行时(runtime)确定使用的类型, 执行不同的函数,复用(reuse)接口. dynamic_cast<>能够 使基类指针转换为派生类的指针, 通过推断指针的类型

MFC六大核心机制之二:运行时类型识别(RTTI)

上一节讲的是MFC六大核心机制之一:MFC程序的初始化,本节继续讲解MFC六大核心机制之二:运行时类型识别(RTTI). typeid运算子 运行时类型识别(RTTI)即是程序执行过程中知道某个对象属于某个类,我们平时用C++编程接触的RTTI一般是编译器的RTTI,即是在新版本的VC++编译器里面选用“使能RTTI”,然后载入typeinfo.h文件,就可以使用一个叫typeid()的运算子,它的地位与在C++编程中的sizeof()运算子类似的地方(包含一个头文件,然后就有一个熟悉好用的函数

RTTI (Run-Time Type Identification,通过运行时类型识别) 转

参考一: RTTI(Run-Time Type Identification,通过运行时类型识别)程序能够使用基类的指针或引用来检查这些指针或引用所指的对象的实际派生类型. RTTI提供了以下两个非常有用的操作符: (1)typeid操作符,返回指针和引用所指的实际类型: (2)dynamic_cast操作符,将基类类型的指针或引用安全地转换为派生类型的指针或引用. 面向对象的编程语言,象C++,Java,delphi都提供了对RTTI的支持. 本文将简略介绍 RTTI 的一些背景知识.描述 R

MFC中的运行时类型识别(RTTI)

RTTI是Runtime Type Identification的缩写,中文名为"运行时类型识别". MFC早在编译器支持RTTI之前,就有了这种能力.我们现在要以相同的手法,在Console程序中仿真出来.我希望我的类库具备IsKindOf 的能力,能在执行期侦测到某个对象是否属于某个类,并传回TRUE 或 FALSE.以形状 Shape为例 ,我希望: 即 长方形属于"长方形类",正方形属于"长方形类",圆不属于"长方形类"

MFC 六大机制 (2) RTTI(运行时类型识别)

RTTI(Runtime Type Identification,运行时类型识别) 程序能够使用基类的指针或引用来检查这些指针或引用所指的对象的实际派生类型.MFC 早在编译器支持 RTTI 之前,就具有了这项能力.承接上一章,我们现在要在 Console 程序中将 RTTI 仿真出来.我希望我的类库具备 IsKindOf() 的能力,能够在执行器检查某个对象是否"属于某种类",并传回 TRUE 或 FALSE.为了更直观地查看结果,我在 IsKindOf() 中加入了输出,使其达到如

Java基础之RTTI 运行时类型识别

运行时类型识别(RTTI, Run-Time Type Identification)是Java中非常有用的机制,在Java运行时,RTTI维护类的相关信息. 多态(polymorphism)是基于RTTI实现的.RTTI的功能主要是由Class类实现的. Class类 Class类是"类的类"(class of classes).如果说类是对象的抽象和集合的话,那么Class类就是对类的抽象和集合. 每一个Class类的对象代表一个其他的类.比如下面的程序中,Class类的对象c1代

RTTI(运行时类型识别)

运行时类型识别(Run-time type identification , RTTI),是指在只有一个指向基类的指针或引用时,确定所指对象的准确类型的操作.其常被说成是C++的四大扩展之一(其他三个为异常.模板和名字空间). 使用RTTI的两种方法: 1.typeid() 第一种就像sizeof(),它看上像一个函数,但实际上它是由编译器实现的.typeid()带有一个参数,它可以是一个对象引用或指针,返回全局typeinfo类的常量对象的一个引用.可以用运算符“= =”和“!=”来互相比较这

C++学习之显示类型转换与运行时类型识别RTTI

static_cast const_cast reinterpret_cast 运行时类型识别(RTTI) dynamic_cast 哪种情况下dynamic_cast和static_cast使用的情况一样? 什么情况下使用dynamic_cast代替虚函数? typeid 命名的强制类型转换形式如下: cast_name<type>(expression); 其中:cast_name指static_cast.dynamic_cast.const_cast.reinterpret_cast中的