zoukankan      html  css  js  c++  java
  • MFC原理第三讲.RTTI运行时类型识别

                  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("是属于这个对象
    ");
        }
        else
        {
            printf("不是属于这个对象
    ");
        }
    
        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 
    "),pCurClass->m_lpszClassName);  //类的名称
            OutputDebugString(str);
            str = "";
            str.Format(TEXT("类的大小 = %d 
    "),pCurClass->m_nObjectSize);
            OutputDebugString(str);
            str = "";
            str.Format(TEXT("类的编号%d 
    "), pCurClass->m_wSchema);
            OutputDebugString(str);
    
            str = "";
            str.Format(TEXT("类的父类指针 %p 
    "), pCurClass->m_pBaseClass);
            OutputDebugString(str);
    
            if (pCurClass->m_pBaseClass != nullptr)
            {
                
                str = "";
                str.Format(TEXT("类的父类名称 %s 
    "), pCurClass->m_pBaseClass->m_lpszClassName);
                OutputDebugString(str);
            }
            
            
            str = TEXT("--------------------------------
    ");
            OutputDebugString(str);
            pCurClass = pCurClass->m_pBaseClass; //一直遍历到顶层位置
        }
    }

    在Ininstance里面调用即可.

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

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

  • 相关阅读:
    虚拟机vmware的连接方式以及IP端口,协议等概念
    python3爬虫--shell命令的使用和firefox firebug获取目标信息的xpath
    numpy的基本用法
    scrapy模拟请求头
    (1)python Scrapy爬虫框架
    flutter ui
    dart 类
    dart 基础
    黑苹果镜像下载地址
    android9.0请求异常
  • 原文地址:https://www.cnblogs.com/iBinary/p/9635206.html
Copyright © 2011-2022 走看看