探索MFC读书笔记——RTTI

RTTI(Runtime Type Identification):运行时类型识别,可以帮助我们在程序运行时知道某个对象是属于哪个类别。

 

一、使用RTTI,需要注意三个地方:

1、编译时需选择/GR选项(/GR的意思是enable C++ RTTI)

2、包含typeinfo.h

3、新的typeid运算子。typeid的参数可以是类型别名,也可以是对象指针。它传回一个typeinf&。

type_info是一个类,定义于typeinfo.h中:

<span style="font-size:14px;">class type_info
{
public:
     virtual ~type_info();
     int operator==(const type_info& rhs) const;
     int operator!=(const type_info& rhs) const;
     int before(const typ_info& rhs) const;
     const char* name() const;
     const char* raw_name() const;
private:
     ...
};</span>

二、RTTI的实现原理

      如果你有一个产品,你想得到产品型号,那么你就要去查找型号对照表。同理如果要在运行时知道某个类的型号,就需要为类添加额外的信息,来记录类的型别信息。

     1MFC中每个类都有一个CRuntimeClass成员变量,充当此额外的记录结构

     CRuntimeClass对象中与RTTI相关的内容:
     

<span style="font-size:14px;">struct CRuntimeClass
{
      LPCSTR m_lpszClassName;
      int m_nObjectSize;
      UINT m_wSchema;
      CObject* (PASCAL* m_pfnCreateObject)();
      CRuntimeClass* m_pBaseClass;
      static CRuntimeClass* pFirstClass;
      CRuntimeClass* m_pNxtClass;
      CObject* CreateObject();
}</span>

     2、DECLARE_DYNAMIC / IMPLEMENT_DYNAMI

(1)、为了将CRuntimeClass对象塞到类中,并声明一个可以抓到该对象地址的函数,MFC定义了DECLARE_DYNAMIC宏:
#define DECLARE_DYNAMIC(class_name)  \

public: \

        static CRuntimeClass class##class_name;\

        virtual CRuntimeClass* GetRuntimeClass() const;

注:宏中的##是告诉编译器将左右的字符串连接在一起。

如:DECLARE_DYNAMIC(CView)

编译器前置处理器编译后的代码为:

 public:

          static CRuntimeClass classCView;

          virtual CRuntimeClass* GetRuntimeClass() const;

(2)、为了将类别记录(各个CRunTimeClass对象)的内容指定以及串接工作最好也嵌入到类中,MFC定义了IMPLEMENT_DYNAMIC宏:

#define IMPLEMENT_DYNAMIC(class_name, base_class_name) \

_IMPLEMENT_RUNTIMECLASS(class_name, base_class_name, 0xFFFF, NULL)

 

_IMPLEMENT_RUNTIMECLASS又是一个宏:

#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;} \

其中:RUNTIME_CLASS(class_name)宏定义为:

      #define RUNTIE_CLASS(class_name) \

             (&class_name::class##class_name)

 

其中:AFX_CLASSINIT是一个结构体,含有一个构造函数,定义如下:

         struct AFX_CLASSINIT

         {

                 AFX_CLASSINIT(CRuntimeClass* pNewClass);

         }

 构造函数定义如下:

        AFX_CLASSINIT::AFX_CLASSINIT(CRuntimeClss* pNewClass)

        {

                pNewClass ->m_pNextClass = CRuntimeClass::pFirstClass;

                CRuntimeClass::pFirstClass = pNewClass;

        }

很明显,该构造函数负责类别信息的串接。

实例:

      // in header file

      class CView  : public CWnd

      {

             DECLARE_DYNAMIC(CView)

             ...

      };

      //in implementation file

      IMPLEMENT_DYNAMIC(CView, CWnd)

上述代码宏展开后是:

      // in header file

      class CView : public CWnd

      {

       public :

                  static CRuntimeClass classCView;

                  virtual CRuntimeCLass* GetRuntimeClass() const;

                  ...  

      }

      //in implementation file

     static char _lpszCView[] = "CView";

     CRuntimeClass CView::classCView = {

            _lpszCView, sizeof(CView), 0xFFFF, NULL, &CWnd::classCWnd, NULL);

     static AFX_CLASSINIT _init_CView(&CView::classCView);

     CRuntimeClass* CView::GetRuntimeClass() const

             {return &CView::classCView; }

 

(3)、注意串行的头部需要特殊处理,所以在根类别CObject中,不能套用现成的宏DECLARE_DYNAMIC和

IMPLEMENT_DYNAMIC,必须特别设计如下:

// in header file
class CObject
{
public:
   virtual CRuntimeClass* GetRuntimeClass() const;
...
public:
   static CRuntimeClass classCObject;
};
// in implementation file
static char szCObject[] = "CObject";
struct CRuntimeClass CObject::classCObject=
{    szCObject, sizeof(CObject), 0xffff, NULL, NULL };
   static AFX_CLASSINIT _init_CObject(&CObject::classCObject);
   CRuntimeClass* CObject::GetRuntimeClass() const
   {
      return &CObject::classCObject;
   }
// in implementation file
CRuntimeClass* CRuntimeClass::pFirstClass= NULL;

 

(4)、关于AFX_CLASSINIT结构体

      关于RTTI宏的实现实际上是没有必要了解的,只要会用,知道是怎么一回事就行了。但是有些设计思想、设计方法却是可以借鉴的。

      AFX_CLASSINIT结构体没有数据成员,只有一个带一个参数的构造函数,在用的时候只是定义一个全局变量(且不会在后续代码中使用它), 它的价值就在于它的构造器,因为在构造器中实现了类别串接。我们发现实际需求是进行“串接”这件事,但是解决方法是“定义一个变量”,在这之前我还没遇到过这种情况,写一个全局函数不就行了吗?为什么要这样做?我想这样做应该是考虑到C++的封装性。

(5)、RTTI既是“串接图”,也是“继承图”

       “串接”的意思是将源文件所有声明和实现的类串接成一个链表,串接次序:头文件中类的定义次序。(所以总体上是基类在前,派生类在后,继承自同一父类的子类的串接次序是先定义的先串接)。

       "继承图"毫无疑问是标记类的继承层次。

       RTTI中既标记了类的串接次序,又标记了类的继承层次,在CRuntimeClass结构体中成员CRuntimeClass* m_pBaseclass起串接类继承层次的作用,成员CRuntimeClass* m_pNextClass起串接类的串接次序。

 

      3、DECLARE_DYNCREATE / IMPLEMENT_DYNCREATE

      动态生成:在程序运行期间,根据动态获得的一个类别(通常来自读文件),要求程序产生一个对象

      有了上述的类型识别,动态生成技术就简单了:根据输入的类别,从类别型录网中查找对应的元素,然后调用记录的构造函数,产生对象。

      注:CRuntimeClass中的函数指针m_pfnCreateObject和成员方法CreateObject()。

(1)、DECLARE_DYNCREATE / IMPLEMENT_DYNCREATE的内容

      #define DECLARE_DYNCREATE(class_name) \
      DECLARE_DYNAMIC(class_name) \
      static CObject* PASCAL CreateObject();

      #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)

     可见,1*、DECLARE_DYNCREATE和IMPLEMENT_DYNCREATE宏中包含DECLARE_DYNAMIC和IMPLEMENT_DYNAMIC宏。

     2*、CreateObject内部实际上是new了一个对象,注意动态创建的对象,在使用完成后要手动调用delete删除

     3*、在MFC应用程序中,CWnd、CFrameWnd、CXXXWnd、CXXXFrameWnd、CXXXDoc、CXXXView都是使用DECLARE_DYNCREATE和IMPLEMENT_DYNCREATE宏,其余的使用DECLARE_DYNAMIC和IMPLEMENT_DYNAMIC宏。

 

三、RTTI的使用

      (1)、类型识别,IsKindOf()

      在CObject中添加IsKindOf成员方法,就可以将参数指定的CRuntimeClass对象与型别录中的元素一一比对,比对成功返回true,比对失败返回false。

<span style="font-size:14px;">BOOL CObject::IsKindOf(const CRuntimeClass* pClass) const
{
	CRuntimeClass* pClassThis = GetRuntimeClass();
	while(pClassThis != NULL)
	{
		if(pClassThis == pClass)
			return TRUE;
		pClassThis = pClassThis->m_pBaseClass;
	}
	return FALSE;
}</span>

     注意:IsKindOf中追踪的是m_pBaseClass。因此可以判断出一个类是否是某种基类型的。

     例:CView* pView = new CView;

            pView ->IsKindOf(RUNTIME_CLASS(CWinApp));//注意这里的输入参数是CWinApp类静态成员classCWinApp的地址。

     函数IsKindOf的执行过程是按照CView、CWnd、CCmdTarget、CObject的循线路径,每获得一个CRuntimeClass对象指针,就拿来和"&CView::classCView"指针比对。直到最顶端的CRuntimeClass对象的m_pBaseClass成员为NULL;返回结果。

    (2)、动态生成对象

     动态生成一个CView对象:

             CObject* pObject = RUNTIME_CLASS(CView)->CreateObject();

    删除该对象:

           if(pObject) {delete pObject; pObject = NULL;}

 

      

你可能感兴趣的:(探索MFC读书笔记——RTTI)