• MFC中动态创建DECLARE_DYNCREATE和运行时类型识别DECLARE_DYNAMIC


    DECLARE_DYNCREATE:支持动态创建
    DECLARE_DYNAMIC:支持运行时类型识别(RTTI)
    凡是支持动态创建的,一定支持RTTI。

    #define DECLARE_DYNCREATE(class_name) 
        DECLARE_DYNAMIC(class_name) 
        static CObject* PASCAL CreateObject();
    
    #define DECLARE_DYNAMIC(class_name) 
    protected: 
        static CRuntimeClass* PASCAL _GetBaseClass(); 
    public: 
        static const CRuntimeClass class##class_name; 
        static CRuntimeClass* PASCAL GetThisClass(); 
        virtual CRuntimeClass* GetRuntimeClass() const; 
    #define IMPLEMENT_DYNCREATE(class_name, base_class_name) 
        CObject* PASCAL class_name::CreateObject() 
            { return new class_name; } 
        IMPLEMENT_RUNTIMECLASS(class_name, base_class_name, 0xFFFF, 
            class_name::CreateObject, NULL)
    
    #define IMPLEMENT_RUNTIMECLASS(class_name, base_class_name, wSchema, pfnNew, class_init) 
        CRuntimeClass* PASCAL class_name::_GetBaseClass() 
            { return RUNTIME_CLASS(base_class_name); } 
        AFX_COMDAT const CRuntimeClass class_name::class##class_name = { 
            #class_name, sizeof(class class_name), wSchema, pfnNew, 
                &class_name::_GetBaseClass, NULL, class_init };  //注:函数名就是地址,即 &func == func
        CRuntimeClass* PASCAL class_name::GetThisClass() 
            { return _RUNTIME_CLASS(class_name); } 
        CRuntimeClass* class_name::GetRuntimeClass() const 
            { return _RUNTIME_CLASS(class_name); }
    
    #define RUNTIME_CLASS(class_name) (class_name::GetThisClass())
    
    #define _RUNTIME_CLASS(class_name) ((CRuntimeClass*)(&class_name::class##class_name))

    所以:

    // MainFrm.h 
    DECLARE_DYNCREATE(CMainFrame)
    翻译为
    DECLARE_DYNAMIC(CMainFrame)
    static CObject* PASCAL CreateObject();
    最终翻译为
    protected: 
        static CRuntimeClass* PASCAL _GetBaseClass(); 
    public: 
        static const CRuntimeClass classCMainFrame; 
        static CRuntimeClass* PASCAL GetThisClass(); 
        virtual CRuntimeClass* GetRuntimeClass() const; 
        static CObject* PASCAL CreateObject();
    
    // MainFrm.cpp
    IMPLEMENT_DYNCREATE(CMainFrame, CFrameWnd)
    翻译为
    CObject* PASCAL CMainFrame::CreateObject() 
            { return new CMainFrame; } 
    IMPLEMENT_RUNTIMECLASS(CMainFrame, CFrameWnd, 0xFFFF, CMainFrame::CreateObject, NULL)
    最终翻译为
    CObject* PASCAL CMainFrame::CreateObject() 
            { return new CMainFrame; } 
    CRuntimeClass* PASCAL CMainFrame::_GetBaseClass() 
            { return CFrameWnd::GetThisClass(); } // return (CRuntimeClass*)(&CFrameWnd::classCMainFrame);
    AFX_COMDAT const CRuntimeClass CMainFrame::classCMainFrame= { "CMainFrame", sizeof(class MainFrame), 0xFFFF, CMainFrame::CreateObject, &CMainFrame::_GetBaseClass, NULL, NULL }; 
    CRuntimeClass* PASCAL CMainFrame::GetThisClass() 
            { return (CRuntimeClass*)(&CMainFrame::classCMainFrame); } 
    CRuntimeClass* CMainFrame::GetRuntimeClass() const 
            { return (CRuntimeClass*)(&CMainFrame::classCMainFrame); }

    CRuntimeClass类的定义

    struct CRuntimeClass
    {
    // Attributes
        LPCSTR m_lpszClassName;
        int m_nObjectSize;
        UINT m_wSchema; // schema number of the loaded class
        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;       // linked list of registered classes
        const AFX_CLASSINIT* m_pClassInit;
    };
    View Code

    可以看出:主要是一个静态成员变量,和四个成员函数。
    静态成员变量classCMainFrame 主要是用于存储本类的信息和基类的信息地址。
    GetRuntimeClass():用于获取本类信息地址,即 &classCMainFrame (GetRuntimeClass()等于GetThisClass() )。
    _GetBaseClass:用于获取基类信息的地址,即 &CFrameWnd::classCMainFrame
    静态成员函数 CreateObject() :用于在堆空间申请对象空间。

    宏定义中 #:表示字符串  ##:表示连接

    在用#define 定义时 , 斜杠("")是用来续行的,

    "#"用来把参数转换成字符串,是给参数加上双引号。
    "##"则用来连接前后两个参数,把它们变成一个参数,
    "#@"是给参数加上单引号。下面的例子会使您很容易理解。
    #define CAT(x,y)           x##y       /* CAT("1","abc") => "1abc" CAT(1, 34) => 134 */
    #define TOCHAR(a)     #@a       /* TOCHAR(1) => '1' */
    #define TOSTRING(x)     #x      /* TOSTRING(1) => "1" */


    参考: https://blog.csdn.net/biblereader/article/details/988100

    本来,在C/C++中有一个typeid操作符(类似于sizeof),它可以判断对象的类型,用法如下:
     Devi objDevi;  //Devi是本人自定义的一个类;

     if (typeid(Devi)==typeid(objDevi))
     {
      cout<<"objDevi is a object of class Devi"<<endl;
     }
     const type_info& tpdevi=typeid(objDevi);
     cout<<tpdevi.name()<<endl;               //这里得出是class Devi的类型,用于人识别
     cout<<tpdevi.raw_name()<<endl;   //这里返回的是内存表示,用于计算机识别

     可惜的是typeid比MFC的RTTI晚出现, MFC中的RTTI没有使用typeid,而是用了上面提到的一系列的宏,什么要求用什么宏,MSDN同样有说明:

    Macros Used for Serialization and Run-Time Information

    Macro used CObject::IsKindOf CRuntimeClass::CreateObject CArchive::operator>> CArchive::operator<<
    Basic CObject functionality No No No
    DECLARE_DYNAMIC Yes No No
    DECLARE_DYNCREATE Yes Yes No
    DECLARE_SERIAL Yes Yes Yes

    仅判断类型,用DECLARE_DYNAMIC足矣, 要动态创建对象,得用 DECLARE_DYNCREATE, 而如果要串行化,则得用 DECLARE_SERIAL宏.
    每个从CObject派生来的类都有一个CRuntimeClass* GetRuntimeClass( ) const函数,用来返回一个CRuntimeClass对象。如何在各个类之中插入CRuntimeClass对象,并且指定CRuntimeClass对象的内容及CRuntimeClass对象的链接?MFC用了两个宏实现了这些工作,即DECLARE_DYNAMIC(类名)和IMPLEMENT_DYNAMIC(类名,基类名)。

    而CObject的另一个函数IsKindOf被用来进行运行时识别工作,自从有了IsKindOf,生活就变得美好了:

    BOOL CObject::IsKindOf(const CRuntimeClass* pClass) const
    {
        CRuntimeClass* pClassThis = GetRuntimeClass();//GetRuntimeClass()是个虚函数,每个从CObject继承的类,并使用了宏DECLARE_DYNAMIC ,都重写了GetRuntimeClass(),根据多态,调用子类中的 GetRuntimeClass(),即返回子类的类信息的地址 (&class***)
        return pClassThis->IsDerivedFrom(pClass);
    }


     而GetRuntimeClass()的作用是返回与该类关联的CRuntimeClass类指针:

    CRuntimeClass* CObject::GetRuntimeClass() const
    {
         return _RUNTIME_CLASS(CObject);// 即 静态成员的地址  &class***
    }


     而这个宏RUNTIME_CLASS 就是用来得到一个static const AFX_DATA CRuntimeClass classCObject.  (每个从CObject派生的类如CMainFrame都有成员classCMainFrame,这个class***成员就是与MFC类相关的那个CRuntimeClass对象!!).

     解决问题的关键还在于上面的IsDerivedFrom函数:

    BOOL CRuntimeClass::IsDerivedFrom(const CRuntimeClass* pBaseClass) const
    {
      const CRuntimeClass* pClassThis = this;
      while (pClassThis != NULL)
      {
         if (pClassThis == pBaseClass) return TRUE; //从pBaseClass派生则返回,否则继续循环,万一不行,则返回FALSE;
         pClassThis = pClassThis->m_pBaseClass;
      }
      return FALSE; // walked to the top, no match
    }

    从宏观上说,IsKindOf函数判断了一个MFC对象(而不是类)的类型!!
    结论: 如果你的类派生自CObject且使用了 DECLARE_DYNAMIC and IMPLEMENT_DYNAMIC宏,那么,你的类就能够调用IsKindOf方法, 而如果调用IsKindOf方法, 则能够获取该类的类型信息(如果不调用IsKindOf函数,自然也就没从获取类型信息了).
    例如:

    BOOL b = pObject->IsKindOf(RUNTIME_CLASS());

    MFC的动态创建过程:
         1、定义一个不带参数的构造函数,因为我们是用CreateObject()动态创建,它只有一条语句就是 return new XXX,不带任何参数。
       2、类说明中使用DECLARE_DYNCREATE(CLASSNMAE)宏;和在类的实现文件中使用IMPLEMENT_DYNCREATE(CLASSNAME,BASECLASS)宏,这个宏完成构造CRuntimeClass对象,并加入到链表中。
            3、使用时先通过宏RUNTIME_CLASS得到类的RunTime信息,然后使用CRuntimeClass的成员函数CreateObject创建一个该类的实例。
       4、CObject* pObject = pRuntimeClass->CreateObject();//完成动态创建
    总结: MFC动态创建的最终动作是CRuntimeClass::CreateObject();获得类型信息只是过程的一环.
    例如:

    CRunTimeClass* prt = RUNTIMR_CLASS(CMyFrame);//CMyFrame是自己创建的继承CMIniFrame的MFC类,会自动添加DECLARE_DYNCREATE等宏
    m_pFrame = (CMyFrame*)prt->CreateObject();//或者 m_pFrame = DYNAMIC_DOWNCASE(CMyFrame, prt->CreateObject()); 实际就是new XXX;但不用自己delete

    可简单地理解为 DECLARE_DYNCREATE 宏是 DECLARE_DYNAMIC 宏与 CreateObject函数的合, DECLARE_SERIAL 是 DECLARE_DYNCREATE  与操作符operator>>的合. 这里的operator>>是IMPLEMENT_SERIAL宏重载的.因此,MSDN中也看到了,要使一个类可串行化,使用DECLARE_SERIAL和IMPLEMENT_SERIAL宏只是一步而已,还要重载Serialize等工作.

     总结: 看CRuntimeClass的源代码知道,CRuntimeClass还有两个函数Load和Store,这两个函数的实现中还调用了CArchive的Read和Write方法,但是要说明的是,CRuntimeClass的Load/Store在文件的串行化中,只是做了一些辅助工作,如判断是否第一次出现,记录版本号,记录文件名等,真正实现串行化的还是CArchive的operator<</operator>>以及其他一些方法.

     CRuntimeClass对象只是MFC类的一个成员变量,它记录了本MFC类的一些信息,帮助本MFC类实现一些功能,仅此而已.

    ***********

    常记溪亭日暮,沉醉不知归路。兴尽晚回舟,误入藕花深处。争渡,争渡,惊起一滩鸥鹭。

    昨夜雨疏风骤,浓睡不消残酒。试问卷帘人,却道海棠依旧。知否?知否?应是绿肥红瘦。
  • 相关阅读:
    bash /root/.bashrc permission denied
    vscode 在ubuntu的terminal中下划线不显示解决方案
    基于SSH框架的考勤管理系统的设计与实现
    关于《实验一》的框架选择
    认知架构
    《软件需求》读书笔记3
    《软件需求》读书笔记1
    《软件需求》读书笔记2
    《软件方法》读书笔记2
    《软件方法》读书笔记3
  • 原文地址:https://www.cnblogs.com/htj10/p/11831404.html
Copyright © 2020-2023  润新知