现在的位置: 首页 > 综合 > 正文

MFC中动态创建对象的一点思考

2013年10月09日 ⁄ 综合 ⁄ 共 2746字 ⁄ 字号 评论关闭

本文对MFC动态创建对象的方法做了一点阐述,给出了一些我认为存在的问题及相应的解决方法,不当及不足之处请各位不吝赐教。这里指的动态创建指给定一个类的名字(字符串),创建出相应的类的实例,类似于java里面的反射机制。

  二、问题描述

  对于给定的类,我们可以采取

CSampClass *m_pSampClass=new CSampClass

  的方法,或稍微麻烦一点

CRuntimeClass *m_pRuntimeClass=RUNTIME_CLASS(CSampClass);
m_pSampClass=(CSampClass *)m_pRuntimeClass->CreateObject();

   我想大家都会毫不犹豫的选择第一种方法,这里给出第二种方法,只是为了下面叙述的方便。

  现在给定的是一个类名字符串"CSampClass",我们当然不能直接用CSampClass *m_pSampClass=new "CSampClass"的方法创建出对象来。同样第二种方法也不行,但它给我们提供了另外一种思路:利用MFC的动态创建机制。

  根据动态创建机制,我们需要在我们的类中加入DECLARE_DYNACREATE和IMPLEMEN_DYNACREATE宏,这两个宏的作用是在我们的类中加入一个CRuntimeClass类型的静态成员变量,然后将其加入一张链表中,构成我们的运行时类别信息表,以支持类的RTTI、DYNACREATE以及SERILIZE。

  现在来看看CRuntimeClass里面到底有些什么东西,以下是其结构,来自MSDN.

struct CRuntimeClass
{
 LPCSTR m_lpszClassName;
 int m_nObjectSize;
 UINT m_wSchema;
 CObject *(PASCAL *m_pfnCreateObject)();
 CRuntimeClass *m_pBaseClass;
 CObject *CreateObject();
 BOOL IsDerivedFrom(Const CRuntimeClass *pBaseClass) Const;
}

  其中,m_lpszClassName是对应的类名,m_pBaseClass是基类对应的CRuntimeClass结构,利用CreateObject我们就能够构建出对应类的实例来。

  提到CRuntimeClass,我们不得不提到一个很重要的宏RUNTIME_CLASS,它接受一个类,并返回该类对应的CRuntimeClass结构,那么看到这里我们不难理解上面创建对象的 第二种方法,并且我们的动态创建好象也有了眉目:我们只需要遍历CRuntimeClass链表,把每个接点的m_lpszClassName和给定的类名字符串比较,如果相等,调用该接点的CreateObject()即可。

  事情到此好象已经完了,真的那么简单吗?

  我们再来检查一下,遍历链表,我们需要知道链表的头吧。头在哪里?RUNTIME_CLASS宏只是提供了我们给定的宏的CRuntimeClass结构,该结构并不是我们所要的头。并且CRuntimeClass结构也没为遍历整个链表提供足够的信息,m_pBaseClass只是为我们提供了按继承关系进行遍历的手段,但这无法支持对整个链表的遍历。好不容易走到这里,线索似乎又断了。

  没办法,翻翻侯捷老师的深入浅出MFC吧。里面P97页讲到动态创建时,用到的CRuntimeClass结构如下:

struct CRuntimeClass
{
 LPCSTR m_lpszClassName;
 int m_nObjectSize;
 UINT m_wSchema;
 CObject *(PASCAL *m_pfnCreateObject)();
 CRuntimeClass *m_pBaseClass;
 CObject *CreateObject();
 BOOL IsDerivedFrom(Const CRuntimeClass *pBaseClass) Const;
 static CRuntimeClass *m_pFirstClass;
 CRuntimeClass *m_pNextClass;
}

  看到最后两个字段,我们不难推想出侯老师用的动态创建算法,不知道微软把这两个字段藏到什么地方去了。

  后来看侯捷老师在深入浅出MFC中介绍SERILIZE时,在P388页有这么一段代码(我自己稍做改编,伪码),很给我启发:

CRuntimeClass *CRuntimeClass::Load(CString szClassName)
{
 CRuntimeClass *pClass=NULL;
 AFX_MODULE_STATE pModuleState=afxGetModuleState();
 ASSERT(!pModuleState->m_classList.IsEmpty()); //我加的断言,调试用
 for (pClass=pModuleState->m_classList;pClass!=NULL;pClass=pClass->m_pNextClass)
  if (lstrcmpa(szClassName,pClass->m_lpszClassName)==0)
   return pClass;
  return NULL;
}

  这段代码好象是说所有的CRuntimeClass结构都放在pModuleState->m_classList,但我在调试是时断言总是不成立,并且pClass=pClass->m_pNextClass这一句也让我困惑不已,MFC里面的CRunTimeClass哪里来的m_pNextClass字段?请高手指点。

  三、解决办法

  既然在MFC里面好象直接找不到办法(也许是我暂时没发现,请高手指教),我们可以采用间接的办法,自己 写一个CRuntimeClassEX类,包装MFC里面的CRuntimeClass,另加两个字段:

struct CRuntimeClassEX
{
 CRuntimeClass *m_pCRunTimeClass;
 static CRuntimeClass *m_pFirstClass;
 CRuntimeClass *m_pNextClass;
}

  同样,需要包装DECLARE_DYNACREATE(DECLARE_DYNACREATEX)和IMPLEMEN_DYNACREATE(IMPLEMEN_DYNACREATEX)两个宏,具体请参见候捷老师在深入浅出MFC里面的方法。我们在动态创建时只需要遍历该链表,找到相应的CRuntimeClass,即可创建出我们的对象来。

抱歉!评论已关闭.