转自:http://www.builder.com.cn/2007/1020/568183.shtml
一、概述
本文对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,即可创建出我们的对象来。