zoukankan      html  css  js  c++  java
  • MFC的RTTI实现

    C++设计者在C++使用的早期并没有意识到RTTI(运行时类型检查)的重要性,后来随作框架结构的类库出现及其应用越来越广泛,RTTI就变得越来越重要了。例如下面的这个语句:
      CWnd *pWnd;
    任何人都知道对象pWnd是CWnd类型的指针。但是如果有一个类CView是从CWnd派生来的,对于下面的语句:
      CWnd* CreateView()
      {
       return new CView;
      }

    对 于使用CreateView()的用户而然,pWnd = CreateView(),他如何确定pWnd所指向的对象的真正类型呢?因此,必须有一个能够在运行时刻就能够确定指针对象类型的方法,比如给每一个类 型的对象均添加一个IsKindOf()之类的方法,通过此方法判断指针对象的类型。

      后来,RTTI被加入了C++的规范,成为C++一个内置的特性。

       在MFC的设计者们设计MFC的时候,C++规范中并没有包含RTTI,但是他们很早就意识到这个问题,所以他们以一种独特的方式在MFC中实现 RTTI,采用这种方式实现的RTTI对于某个对象而言并不是必须的,也就是说,MFC的设计者们并不将RTTI强加于用户所设计的类型上,而是让用户根 据自己的需要选择是否他所设计的类型需要RTTI。因而这种方式比C++规范中内置的RTTI更灵活。

      MFC的设计者们在MFC中采用下面的的方法来实现RTTI:

       设计一个基类CObject,在CObject中增加RTTI功能,任何一个类型,如果需要具有RTTI功能,就必须直接或间接派生于CObject采 用宏实现RTTI,对于某个直接或间接从CObject派生来的类型而言,该宏可有可无,如果有该宏,它就具有RTTI功能,反之则无。

          考察CObject
      我们先从CObject开始,下面是它的定义:

      class AFX_NOVTABLE CObject
      {
       public:
        // Object model (types, destruction, allocation)
        virtual CRuntimeClass* GetRuntimeClass() const;
        virtual ~CObject(); // virtual destructors are necessary
      
              // Diagnostic allocations
        void* PASCAL operator new(size_t nSize);
        void* PASCAL operator new(size_t, void* p);
        void PASCAL operator delete(void* p);
        void PASCAL operator delete(void* p, void* pPlace);
        void PASCAL operator delete(void *p, LPCSTR lpszFileName, int nLine);

        // Disable the copy constructor and assignment by default so you will get
        // compiler errors instead of unexpected behaviour if you pass objects
        // by value or assign objects.

       protected:
        CObject();
       private:
        CObject(const CObject& objectSrc); // no implementation
           void operator=(const CObject& objectSrc); // no implementation

       // Attributes
       public:
        BOOL IsSerializable() const;
        BOOL IsKindOf(const CRuntimeClass* pClass) const;
     
           // Overridables
        virtual void Serialize(CArchive& ar);
        // Implementation
       public:
        static const AFX_DATA CRuntimeClass classCObject;
      };

    总的来说,CObject定义了整个从其派生的家族的所有成员所具有的两个基本的能力:

      运行时的动态类型检查(RTTI)能力和序列化能力。在早期的C++版本中,没有规定RTTI,但MFC的作者们早就未扑先知,以这种构架的形式定义并实现RTTI。体现RTTI的是CObject中的两个成员函数:

      virtual CRuntimeClass * GetRuntimeClass() const;
      BOOL IsKindOf(const CRuntimeClass *pClass) const;

    其中,前一个函数用来访问存储RTTI信息的一个CRuntimeClass类型的结构,后一个函数供在运行时刻进行类型判断。我们先来看看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
          CRuntimeClass* m_pBaseClass;
          
                // Operations
             CObject* CreateObject();
          BOOL IsDerivedFrom(const CRuntimeClass* pBaseClass) const;

          // 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
      };

    上 面就是CRuntimeClass的定义,m_lpszClassName保存类的名称,m_nObjectSize保存类的实例数据所占内存的的大小。 我们重点要关注的是m_pBaseClass成员,它是指向名称为m_lpszClassName的类的基类的CRuntimeClass的指针,因此, CRuntimeClass就形成了一个继承链表,这个链表记录了某一族类的继承关系。

     RTTI的实现:

      实现RTTI的除了上面两个函数外,还有几个相关的宏。我们先看看GetRuntimeClass()和IsKindOf()的实现.

      1.GetRuntimeClass()的实现
      CRuntimeClass* CObject::GetRuntimeClass() const
      {
       return RUNTIME_CLASS(CObject);
      }

      关键就在RUNTIME_CLASS这个宏上,RUNTIME_CLASS宏的实现如下:

      #define RUNTIME_CLASS(class_name) ((CRuntimeClass*)(&class_name::class##class_name))将宏展开,上面的实现就变成:
      CRuntimeClass* CObject::GetRuntimeClass() const
      {
       return (CRuntimeClass*)(&CObject::classCObject);
      }

    也就是说,它返回CObject类的一个static型的成员classCObject。

      2.IsKindOf()的实现
      BOOL CObject::IsKindOf(const CRuntimeClass* pClass) const
      {
       ASSERT(this != NULL);
       // it better be in valid memory, at least for CObject size
       ASSERT(AfxIsValidAddress(this, sizeof(CObject)));

       // simple SI case
       CRuntimeClass* pClassThis = GetRuntimeClass();
       return pClassThis->IsDerivedFrom(pClass);
      }

            前两行我们不管它,关键在于最后一行pClassThis->IsDerivedFrom(pClass),归根结底就是调用 CRuntimeClass的IsDerivedFrom()方法。下面是CRuntimeClass的成员IsDerivedFrom()的实现:
      BOOL CRuntimeClass::IsDerivedFrom(const CRuntimeClass* pBaseClass) const
      {
       ASSERT(this != NULL);
       ASSERT(AfxIsValidAddress(this, sizeof(CRuntimeClass), FALSE));
       ASSERT(pBaseClass != NULL);
       ASSERT(AfxIsValidAddress(pBaseClass, sizeof(CRuntimeClass), FALSE));

       // simple SI case
       const CRuntimeClass* pClassThis = this;
       while (pClassThis != NULL)
          {
           if (pClassThis == pBaseClass) return TRUE;
           pClassThis = pClassThis->m_pBaseClass;
             }
       return FALSE; // walked to the top, no match
      }

      关键是上面的一段循环代码:
      while (pClassThis != NULL)
      {
       if (pClassThis == pBaseClass) return TRUE;
       pClassThis = pClassThis->m_pBaseClass;
      }

            它从继承链表的某一节点this开始,向后搜索比较,确定继承关系。

            到这里,或许有人要问,这些CRuntimeClass结构是如何产生的呢?这是一个很好的问题,解决了这个问题,就完全清楚了MFC中RTTI的实现。 使用过Visual C++开发程序的人都应该记得DECLARE_DYNAMIC和IMPLEMENT_DYNAMIC这两个宏,它们分别用来定义相应类的static CRuntimeClass成员和对该成员初始化。

      DECLARE_DYNAMIC宏的定义:
      #define DECLARE_DYNAMIC(class_name) \
      public: \
      static const AFX_DATA CRuntimeClass class##class_name; \
      virtual CRuntimeClass* GetRuntimeClass() const; \

      例如DECLARE_DYNAMIC(CView)展开成为:
      public:
       static const AFX_DATA CRuntimeClass classCView;
       virtual CRuntimeClass* GetRuntimeClass() const;

            由此可见,DECLARE_DYNAMIC宏用来在类的定义中定义静态CRuntimeClass变量和虚拟GetRuntimeClass()函数。可以 推断,IMPLEMENT_DYNAMIC宏一定是用来初始化该静态变量和实现GetRuntimeClass()函数,。不错,正是这样!

      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) \
              AFX_COMDAT const AFX_DATADEF CRuntimeClass class_name::class##class_name = { \
              #class_name, sizeof(class class_name), wSchema, pfnNew, \
              RUNTIME_CLASS(base_class_name), NULL }; \
                    CRuntimeClass* class_name::GetRuntimeClass() const \
              { return RUNTIME_CLASS(class_name); } \

            例如IMPLEMENT_DYNAMIC(CView, CWnd)展开如下:
      file://下面展开的代码用来初始化静态CRuntimeClass变量

      AFX_COMDATA const AFX_DATADEF CRuntimeClass CView::classCView =
      {
       “CView”, file://m_lpszClassName
       sizeof(class CView), file://m_nObjectSize
       0xffff, file://m_wSchema
       NULL, file://m_pfnCreateObject
       (CRuntimeClass*)(&CWnd::classCWnd), file://m_pBaseClass
       NULL file://m_pNextClass
      }

      file://下面的代码用来实现GetRuntimeClass()函数
      CRuntimeClass* CView::GetRuntimeClass() const
      {
                     return (CRuntimeClass*)(&CView::classCView);
            }

            总的来说,同RTTI有关的宏有下面几对:

      DECLARE_DYNAMIC和IMPLEMENT_DYNAMIC

            这一对宏能够提供运行是类型判断能力。(定义并实现IsKindOf())

      DECLARE_DYNCREATE和IMPLEMENT_DYNCREATE

            这一对宏除了能够提供类型判断能力外,还能够提供动态创建对象的能力.(定义并实现IsKindOf()和CreateObject())

      DECLARE_SERIAL和IMPLEMENT_SERIAL

    这一对宏除了提供类型判断能力、动态创建对象能力外,还具有序列化功能。(定义并实现IsKindOf()、CreateObject()和Serialize())

  • 相关阅读:
    Unique Binary Search Trees 解答
    Unique Paths II 解答
    Unique Paths 解答
    Maximum Subarray 解答
    Climbing Stairs 解答
    House Robber II 解答
    House Robber 解答
    Valid Palindrome 解答
    Container With Most Water 解答
    Remove Duplicates from Sorted List II 解答
  • 原文地址:https://www.cnblogs.com/dsky/p/2520968.html
Copyright © 2011-2022 走看看