MFC原理第三讲.RTTI运行时类型识别

一丶什么是RTTI

    RTTI. 运行时的时候类型的识别. 运行时类型信息程序.能够使用基类(父类)指针 或者引用 来检查这些指针或者引用所指的对象. 实际派生的类型

  简单来说就是 使用父类指针检查这个对象是属于哪个类.

1.本篇博客需要弄清的问题

  1.1 MFC为什么要构建RTTI

  1.2 DECLARE_DYNAMIC 宏

  1.3 IMPLEMENT_DYNAMIC 宏

  1.4 RUNTIME_CLASS 宏

  1.5 CRuntime Class 结构体

2.简单了解关键字的使用

  1.类中定义的static的变量的作用以及怎么初始化.

  2.类中定义的const的变量的作用以及怎么初始化

  3.类中的 static + const定义的变量的作用以及怎么初始化.

二丶C++简单的RTTI运行类型识别

  在讲解我们要搞清楚的问题的时候.写一个简单的小例子. 使用C++自带的 编译时的RTTI程序.  注意是编译时.

具体做法:

  1. 要使用typeid 关键字  typeid(对象)  typeid (类名)

  2.首先要包含头文件 <typeinfo.h>

  3.启动C++自己的类型.  属性 -> 配置属性 -> C/C++ -> 语言 -> 启用运行时类型信息    命令行加的是 /GR 我们也可以直接加/GR

例如下图:

  

1. 首先我们创建两个类. 一个是 CDog 一个是CCat.

2.然后我们的Main函数 定义 CDog 对象以及CCat对象. 并且判断 CDog对象是否属于CDog这个类.

3.如果属于我们则进行打印.否则相反.

具体代码:

  

#include <stdio.h>
#include <typeinfo.h>
#include "Dog.h"
#include "Cat.h"
int main(int argc, char *argv[])
{
CDog dog;
CCat cat;
if (typeid(dog) == typeid(CDog)) 判断dog对象是否属于 CDog类 主要就是这行代码
{
printf("是属于这个对象\r\n");
}
else
{
printf("不是属于这个对象\r\n");
} system("pause");
}

实现应用截图:

  

这个就是简单的RTTI运行时类型识别了.

三丶类中的 static关键字.const关键字 static + const 关键字的作用以及初始化

  1.static关键字 修饰的变量.外部进行初始化.并且不依赖与对象.也就是说直接  类名::变量  则可以调用   类型  类名::成员变量  = 值;

  2.const关键字修饰的变量只能读不能改.  初始化的时候必须在 类的构造的初始化列表进行初始化.

  3.static + const修饰的变量. 只能读不能改. 类名直接调用.  初始化的时候在外部进行初始化  const 类型  类名::成员变量  = 值;

四丶MFC为什么自己构建RTTI

  MFC因为出现的年代比较早.所以自己实现了RTTI. 而且依靠的就是两个宏

  1.2 的 DECLARE_DYNAMIC 宏

  1.3 的 IMPLEMENT_DYNAMIC 宏

其中1.3里面的宏也包含了一个关键的宏 RUNTIME_CLASS 以及关键结构体 CRuntime Class

一丶使用1.2 宏 1.3宏.

  现在我们要让我们自己的类拥有RTTI运行时类型识别.

  步骤:

    1.我们的自己的WinApp 类里面 定义 DECLARE_DYNAMIC宏

    2.main函数之前使用 IMPLEMENT_DYNAMIC 宏

    3.使用 IsKindOf(CRuntime Class *) 来判断是否是继承父类. 因为是 CRuntime Class * 所以 我们要使用宏包含我们的 父类 RUNTIME_CLASS(父类)

    4.如果是继承父类 则返回1. 否则 返回0

知道步骤了.那么打开第一篇博客的代码. 自己实现的窗口程序.去编写.

  1.自己类里面定义DECLARE_DYNAMIC宏 截图:

    

   2.自己类的实现文件中 定义IMPLEMENT_DYNAMIC(自己的类名,父类类名)

     

  3.自己的类已经拥有了Rtti 类型识别.使用Rtii 运行识别.

二丶 RUNTIME_CLASS 宏解析

  上面我们使用 RUNTIME_CLASS 宏来做使用那么我们看一下MFC怎么定义的.

#define _RUNTIME_CLASS(class_name) ((CRuntimeClass*)(&class_name::class##class_name))

其实也是一个宏.不过我们可以拆解一下.

  

#define _RUNTIME_CLASS(CWinApp) ((CRuntimeClass*)(&CWinApp::classCWinApp))

 其中 ##代表了链接的意思 也就是 Class##ClassName 可以变成  ClassWinApp

  所以上面我们用宏写的代码下方可以替换成我们解析出来的宏.

返回了一个 CRuntime Class * . 返回的是CWinAPP里面的一个成员的地址.因为前边有一个取地址符号..

首先看一下CRuntimeClass结构体吧

三丶CRuntimeClass结构体

struct CRuntimeClass
{
// Attributes
LPCSTR m_lpszClassName; 类名
int m_nObjectSize; 类的大小
UINT m_wSchema; 加载类的编号
CObject* (PASCAL* m_pfnCreateObject)(); // NULL => abstract class
#ifdef _AFXDLL
CRuntimeClass* (PASCAL* m_pfnGetBaseClass)();
#else
CRuntimeClass* m_pBaseClass;
#endif // Operations
CObject* CreateObject();
BOOL IsDerivedFrom(const CRuntimeClass* pBaseClass) const; 判断函数 // dynamic name lookup and creation
static CRuntimeClass* PASCAL FromName(LPCSTR lpszClassName);
static CRuntimeClass* PASCAL FromName(LPCWSTR lpszClassName);
static CObject* PASCAL CreateObject(LPCSTR lpszClassName);
static CObject* PASCAL CreateObject(LPCWSTR lpszClassName); // Implementation
void Store(CArchive& ar) const;
static CRuntimeClass* PASCAL Load(CArchive& ar, UINT* pwSchemaNum); // CRuntimeClass objects linked together in simple list
CRuntimeClass* m_pNextClass; // 执向下一个CRunTimeClass
const AFX_CLASSINIT* m_pClassInit;
};

因为上面成员较多.下方简化一下.

  

struct CRuntimeClass
{
// Attributes
LPCSTR m_lpszClassName; 类名
int m_nObjectSize; 类的大小
UINT m_wSchema; 加载类的编号
...
BOOL IsDerivedFrom(const CRuntimeClass* pBaseClass) const; 判断函数是否是父类
...
CRuntimeClass* m_pNextClass; // 执向下一个CRunTimeClass };

这个是一个链表.  是记录类型的一个结构. 因为是链表.所以可以进行检查.

四丶DECLARE_DYNAMIC 宏解析

  其实 DECLARE_DYNAMIC 宏也是一个文字替换的东西.我们可以看下代码.

#define DECLARE_DYNAMIC(class_name) \
public: \
static const CRuntimeClass class##class_name; \
virtual CRuntimeClass* GetRuntimeClass() const; \ 替换后是下边的代码. public:
static const CRuntimeClass classCMyApp;
virtual CRuntimeClass* GetRuntimeClass() const;

在我们的CMyAPP 里面的宏则可以直接去掉了.

做的事情;

  1.申明了 static const 全局可读的变量. 也就是一个 类型记录信息结构体 CRuntimeClass 结构

     问题:  既然是static const定义的.那么肯定是在外面进行初始化的.也就是我们的

IMPLEMENT_DYNAMIC 宏.而这个宏内部还包含了宏.并且对我们添加了默认参数.

#define IMPLEMENT_RUNTIMECLASS(class_name, base_class_name, wSchema, pfnNew, class_init) \
AFX_COMDAT const CRuntimeClass class_name::class##class_name = { \
#class_name, sizeof(class class_name), wSchema, pfnNew, \
RUNTIME_CLASS(base_class_name), NULL, class_init }; \
CRuntimeClass* class_name::GetRuntimeClass() const \
{ return RUNTIME_CLASS(class_name); }

我们简化一下. 其实也是一个文字替换游戏.

const CRuntimeClass CMyApp::classCMyApp =
{
"CMyApp", sizeof(class CMyApp), NULL, NULL,
RUNTIME_CLASS(CWinApp), NULL, NULL
};
CRuntimeClass* CMyApp::GetRuntimeClass() const
{
return RUNTIME_CLASS(CMyApp);
}

其实就是对我们的static + const 变量进行初始化.

上图内部还有一个RUNTIME_CLASS 我们因为上面解析过了RUNTIME_CLASS 知道了.其实是获取 CWinAPP::classCWinapp 成员.

所以可以继续进行替换.

替换完如下.

  

const CRuntimeClass CMyApp::classCMyApp =
{
"CMyApp", sizeof(class CMyApp), NULL, NULL,
(CRuntimeClass*)(&CWinApp::classCWinApp), NULL, NULL
};
CRuntimeClass* CMyApp::GetRuntimeClass() const
{
return(CRuntimeClass*)(&CMyApp::classCWinApp);
}

代码截图:

  

我们的IMPLEMENT_DYNAMIC宏就等价于下面的代码了.

  1. 初始化类内部的 CRuntimeClass 结构的成员.

  2.添加了一个获取父类的ClassCWinAPP的成员了.

五丶RTTI总结

  根据第四小节.我们已经把所有宏的本身模样还原出来了.看的杂乱无章.但是总结一下很简单.

  1.  首先有一个结构叫做CRuntimeClass. 里面存储了类型说明. 比如类名称.大小. 以及判断是否是父类....

  2. 有一个宏叫做 DECLARE_DYNAMIC宏. 这个宏就是定义了一个 自己的一个CRuntimeClass 结构的成员.并且添加了一个获取自己这个成员的一个虚函数.

  3. 实现宏IMPLEMENT_DYNAMIC 其实就是对DECLARE_DYNAMIC 中定义的CRuntimeClass成员进行初始化.  并且实现了 获取自己这个成员的虚函数.

六丶RTTI中运行时类型识别的方法解析

  上方我们讲了RTTI 以及CRuntimeClass 以及两个宏的总结. 那么我们要使用就是使用 isKindOf来使用. 那么解析下这个函数怎么使用了.

    1.取出我们自己当前对象的 ClassCMyWinApp 指针.

    2.循环遍历是否等于父类

    3.不等于父类则进行遍历. 因为CRuntimeClass是一个链表结构. 一直进行遍历.地址比较.如果是则返回True

因为代码截图比教麻烦.所以直接贴里面的核定代码了.

    

BOOL CObject::IsKindOf(const CRuntimeClass* pClass) const
{
....
CRuntimeClass* pClassThis = GetRuntimeClass(); 获取自己的CRuntimeClass 成员 ...
return pClassThis->IsDerivedFrom(pClass); 进行判断.
} BOOL CRuntimeClass::IsDerivedFrom(const CRuntimeClass* pBaseClass) const
{
其余代码省略
const CRuntimeClass* pClassThis = this; 得到字节的CRuntimeClass成员 while (pClassThis != NULL) 循环遍历不是NULL
{
if (pClassThis == pBaseClass) 如果是父类则返回TRUE
return TRUE;
pClassThis = pClassThis->m_pBaseClass; 否则等于父类指针.继续遍历判断
}
return FALSE; // 没有返回FALSE
}

七丶编写代码打印输出父类信息.以及继承关系

    既然我们自己类的内部存储着CRuntimeClass *的指针. 而且是一个链表. 它父类也有自己的. 那么完全可以进行遍历.打印出父类的信息.

实现思路:

    1.获取自己的CRuntimeClass *指针

    2.遍历CRuntimeClass 成员是否为NULL

    3.不为NULL 依次打印出结构体的内容

    4. 当前的CRunTimeClass 指针修改为父类的CRuntimeClass * 指针

具体代码实现:

  

void CMyApp::PrintParentRuntimeInfo()
{
//1.获取自己当前的CRuntimeClass信息. 进行遍历.
CRuntimeClass *pCurClass = GetRuntimeClass();
//进行循环
CString str;
while (NULL != pCurClass)
{
str = "";
str.Format(TEXT("类的名称 = %s \r\n"),pCurClass->m_lpszClassName); //类的名称
OutputDebugString(str);
str = "";
str.Format(TEXT("类的大小 = %d \r\n"),pCurClass->m_nObjectSize);
OutputDebugString(str);
str = "";
str.Format(TEXT("类的编号%d \r\n"), pCurClass->m_wSchema);
OutputDebugString(str); str = "";
str.Format(TEXT("类的父类指针 %p \r\n"), pCurClass->m_pBaseClass);
OutputDebugString(str); if (pCurClass->m_pBaseClass != nullptr)
{ str = "";
str.Format(TEXT("类的父类名称 %s \r\n"), pCurClass->m_pBaseClass->m_lpszClassName);
OutputDebugString(str);
} str = TEXT("--------------------------------\r\n");
OutputDebugString(str);
pCurClass = pCurClass->m_pBaseClass; //一直遍历到顶层位置
}
}

在Ininstance里面调用即可.

实现结果截图: 使用DebugView获取信息.

课堂代码: 链接:https://pan.baidu.com/s/1wp6pTwsSR8QOZo0t3vNzYQ 密码:2y2o

MFC原理第三讲.RTTI运行时类型识别的更多相关文章

  1. 框架原理第二讲,RTTI,运行时类型识别.(以MFC框架讲解)

    框架原理第二讲,RTTI,运行时类型识别.(以MFC框架讲解) 一丶什么是RTTI,以及RTTI怎么设计 通过第一讲,我们知道了怎么样升成一个窗口了,以及简单的消息循环. 第二讲则是主要讲解RTTI ...

  2. RTTI 运行时类型识别 及异常处理

    RTTI   运行时类型识别 typeid  ------  dynamic_cast dynamic_cast 注意事项: 1.只能应用于指针和引用之间的转化 2.要转换的类型中必须包含虚函数 3. ...

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

    运行时类型识别(RTTI, Run-Time Type Identification)是Java中非常有用的机制,在Java运行时,RTTI维护类的相关信息. 多态(polymorphism)是基于R ...

  4. RTTI(运行时类型识别)

    C++为了能够在运行时正确判断一个对象确切的类型,加入了RTTI和type_info. type_info 为每一个类型增加一个type_info对象. 为了能够在运行时获得对象的类型信息type_i ...

  5. RTTI(运行时类型识别),typeid,dynamic_cast

    dynamic_cast注意: 1.只能应用于指针和引用的转换: 2.要转换的类型中必须包含虚函数: 3.转换成功则返回地址,如果失败则返回NULL: 参见项目:RTTI

  6. 框架原理第三讲,RTTCreate,运行时类型创建.(以MFC框架讲解)

    框架原理第三讲,RTTCreate,运行时类型创建.(以MFC框架讲解) 通过昨天的讲解,我们已经理解了运行时类型识别是什么. 比如  CObject * pthis = (Cobject *)Cre ...

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

    上一节讲的是MFC六大核心机制之一:MFC程序的初始化,本节继续讲解MFC六大核心机制之二:运行时类型识别(RTTI). typeid运算子 运行时类型识别(RTTI)即是程序执行过程中知道某个对象属 ...

  8. C/C++杂记:运行时类型识别(RTTI)与动态类型转换原理

    运行时类型识别(RTTI)的引入有三个作用: 配合typeid操作符的实现: 实现异常处理中catch的匹配过程: 实现动态类型转换dynamic_cast. 1. typeid操作符的实现 1.1. ...

  9. 【转载】C/C++杂记:运行时类型识别(RTTI)与动态类型转换原理

    原文:C/C++杂记:运行时类型识别(RTTI)与动态类型转换原理 运行时类型识别(RTTI)的引入有三个作用: 配合typeid操作符的实现: 实现异常处理中catch的匹配过程: 实现动态类型转换 ...

随机推荐

  1. 《Linux就该这么学》第十二天课程

    使用ssh服务管理远程主机 绑定两块网卡 原创地址:https://www.linuxprobe.com/chapter-09.html 第1步:在虚拟机系统中再添加一块网卡设备,请确保两块网卡都处在 ...

  2. 第四范式涂威威:AutoML技术现状与未来展望

    以下内容是对AutoML技术现状与未来展望讲座的总结. 1.机器学习定义 <西瓜书>中的直观定义是:利用经验来改善系统的性能.(这里的经验一般是指数据) Mitchell在<Mach ...

  3. Spring流行的十大理由

    Spring大概是每个JAVA程序员都听过的框架,但是它为什么能这么流行? 听到咕泡学院的Tom老师的公开课,下面是他总结的阿里为什么选择Spring的十大理由,我觉得这也是Spring能流行的原因: ...

  4. eclipse 无法记住svn密码

    每次要求输入密码,很恼人.经过一番折腾,在stackoverflow上找到了解决方案,上面大神果然多 简单的说,就是通过查看工作空间的日志文件,发现报了个java异常,缺少一个class文件(org. ...

  5. How to enable C development in a Windows 10 development environment VM

    To enable C development in a Windows 10 development environment VM, follow these steps: Start VS in ...

  6. 【转载】 .NET框架设计—常被忽视的C#设计技巧

    阅读目录: 1.开篇介绍 2.尽量使用Lambda匿名函数调用代替反射调用(走进声明式设计) 3.被忽视的特性(Attribute)设计方式 4.扩展方法让你的对象如虎添翼(要学会使用扩展方法的设计思 ...

  7. C++ STL常用知识

    模板(各种类型通用): template<class 模板名> 注意:若要使用模板,在每个自定义函数前都必须加上此定义. 排序(algorithm头文件): sort(头指针l,尾指针r) ...

  8. 洛谷P1886--滑动窗口(单调队列模板)

    https://www.luogu.org/problemnew/show/P1886 单调队列的操作上比普通队列多了可以从尾端出队 单调队列保持队内元素单调递增/递减,以保证队首元素为最小/最大元素 ...

  9. sflow介绍与安装过程

    介绍: sFlow技术是一种以设备端口为基本单元的数据流随机采样的流量监控技术,不仅可以提供完整的第二层到第四层甚至全网范围内的实时流量信息,而且可以适应超大网络流量(如大于10Gbit/s)环境下的 ...

  10. jQuery常用选择器总结

    jQuery常用选择器总结: 我们都知道jQuery是JavaScript(JS)的框架,它的语法简单使用方便,被广大开发人员青睐.现在我就它常用的并且十分强大的选择器的方式,做一个总结.鉴于它的选择 ...