在编程过程中,有可能遇到想根据传入的字符串创建相应的对象,在VC7下可以实现,VC6下MFC没有实现该功能。
大家首先要明白,MFC中实现动态创建关键是在于CRuntimeClass类,可以参考《深入浅出MFC》,废话少说,先看CRuntimeClass类。
VC7中CRuntimeClass类中增加了如下函数:
struct CRuntimeClass
{
...
// 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);
...
}
函数定义:
// Runtime class serialization code
CObject* PASCAL CRuntimeClass::CreateObject(LPCSTR lpszClassName)
{
// attempt to find matching runtime class structure
CRuntimeClass* pClass = FromName(lpszClassName);
if (pClass == NULL)
{
// not found, trace a warning for diagnostic purposes
TRACE(traceAppMsg, 0, "Warning: Cannot find %hs CRuntimeClass. Class not defined.\n",
lpszClassName);
return NULL;
}
// attempt to create the object with the found CRuntimeClass
CObject* pObject = pClass->CreateObject();
return pObject;
}
CObject* PASCAL CRuntimeClass::CreateObject(LPCWSTR lpszClassName)
{
USES_CONVERSION;
return CRuntimeClass::CreateObject(W2A(lpszClassName));
}
CRuntimeClass* PASCAL CRuntimeClass::FromName(LPCSTR lpszClassName)
{
CRuntimeClass* pClass;
// search app specific classes
AFX_MODULE_STATE* pModuleState = AfxGetModuleState();
AfxLockGlobals(CRIT_RUNTIMECLASSLIST);
for (pClass = pModuleState->m_classList; pClass != NULL;
pClass = pClass->m_pNextClass)
{
if (lstrcmpA(lpszClassName, pClass->m_lpszClassName) == 0)
{
AfxUnlockGlobals(CRIT_RUNTIMECLASSLIST);
return pClass;
}
}
AfxUnlockGlobals(CRIT_RUNTIMECLASSLIST);
#ifdef _AFXDLL
// search classes in shared DLLs
AfxLockGlobals(CRIT_DYNLINKLIST);
for (CDynLinkLibrary* pDLL = pModuleState->m_libraryList; pDLL != NULL;
pDLL = pDLL->m_pNextDLL)
{
for (pClass = pDLL->m_classList; pClass != NULL;
pClass = pClass->m_pNextClass)
{
if (lstrcmpA(lpszClassName, pClass->m_lpszClassName) == 0)
{
AfxUnlockGlobals(CRIT_DYNLINKLIST);
return pClass;
}
}
}
AfxUnlockGlobals(CRIT_DYNLINKLIST);
#endif
return NULL; // not found
}
CRuntimeClass* PASCAL CRuntimeClass::FromName(LPCWSTR lpszClassName)
{
USES_CONVERSION_EX;
LPCSTR pszClassName = W2A_EX(lpszClassName, _ATL_SAFE_ALLOCA_DEF_THRESHOLD);
if( pszClassName == NULL )
return NULL;
return CRuntimeClass::FromName( pszClassName );
}
其实在VC6中的CRuntimeClass类的Load函数中就可以发现FromName函数的身影,微软只不过是将其中的部分代码提取出来作为单独的FromName函数,看下面Load函数。
// Runtime class serialization code
CRuntimeClass* PASCAL CRuntimeClass::Load(CArchive& ar, UINT* pwSchemaNum)
// loads a runtime class description
{
...
// search app specific classes
AFX_MODULE_STATE* pModuleState = AfxGetModuleState();
AfxLockGlobals(CRIT_RUNTIMECLASSLIST);
for (pClass = pModuleState->m_classList; pClass != NULL;
pClass = pClass->m_pNextClass)
{
if (lstrcmpA(szClassName, pClass->m_lpszClassName) == 0)
{
AfxUnlockGlobals(CRIT_RUNTIMECLASSLIST);
return pClass;
}
}
AfxUnlockGlobals(CRIT_RUNTIMECLASSLIST);
#ifdef _AFXDLL
// search classes in shared DLLs
AfxLockGlobals(CRIT_DYNLINKLIST);
for (CDynLinkLibrary* pDLL = pModuleState->m_libraryList; pDLL != NULL;
pDLL = pDLL->m_pNextDLL)
{
for (pClass = pDLL->m_classList; pClass != NULL;
pClass = pClass->m_pNextClass)
{
if (lstrcmpA(szClassName, pClass->m_lpszClassName) == 0)
{
AfxUnlockGlobals(CRIT_DYNLINKLIST);
return pClass;
}
}
}
AfxUnlockGlobals(CRIT_DYNLINKLIST);
#endif
TRACE1("Warning: Cannot load %hs from archive. Class not defined.\n",
szClassName);
return NULL; // not found
}
下面看看MFC中动态创建宏和序列化宏:
一.MFC中动态创建宏组:
#define DECLARE_DYNCREATE(class_name)
#define IMPLEMENT_DYNCREATE(class_name, base_class_name)
1.DECLARE_DYNCREATE展开:
protected:
static CRuntimeClass* PASCAL _GetBaseClass();
public:
static const AFX_DATA CRuntimeClass classCMyClass;
virtual CRuntimeClass* GetRuntimeClass() const;
static CObject* PASCAL CreateObject();
2.IMPLEMENT_DYNCREATE展开:
CObject* PASCAL CMyClass::CreateObject()
{ return new CMyClass; }
CRuntimeClass* PASCAL CMyClass::_GetBaseClass()
{ return RUNTIME_CLASS(CBaseClass); }
AFX_COMDAT const AFX_DATADEF CRuntimeClass CMyClass::classCMyClass =
{"CMyClass", sizeof(class CMyClass),0xFFFF, NULL,&CMyClass::_GetBaseClass, NULL };
CRuntimeClass* CMyClass::GetRuntimeClass() const
{ return RUNTIME_CLASS(CMyClass); }
二.MFC中序列化宏组:
#define DECLARE_SERIAL(class_name)
#define IMPLEMENT_SERIAL(class_name, base_class_name, wSchema)
1.DECLARE_SERIAL展开:
protected:
static CRuntimeClass* PASCAL _GetBaseClass();
public:
static AFX_DATA CRuntimeClass classCMyClass;
virtual CRuntimeClass* GetRuntimeClass() const;
static CObject* PASCAL CreateObject();
AFX_API friend CArchive& AFXAPI operator>>(CArchive& ar, CMyClass* &pOb);
2.IMPLEMENT_SERIAL展开:
CObject* PASCAL CMyClass::CreateObject()
{ return new CMyClass; }
CRuntimeClass* PASCAL CMyClass::_GetBaseClass()
{ return RUNTIME_CLASS(CBaseClass); }
AFX_COMDAT AFX_DATADEF CRuntimeClass CMyClass::classCMyClass =
{"CMyClass", sizeof(class CMyClass), 1, CMyClass::CreateObject,&CMyClass::_GetBaseClass, NULL };
CRuntimeClass* CMyClass::GetRuntimeClass() const
{ return RUNTIME_CLASS(CMyClass); }
AFX_CLASSINIT _init_CMyClass(RUNTIME_CLASS(CMyClass));
CArchive& AFXAPI operator>>(CArchive& ar, CMyClass* &pOb)
{ pOb = (CMyClass*) ar.ReadObject(RUNTIME_CLASS(CMyClass));return ar; }
这两组宏中影响动态创建的两个关键的地方为:
1. DECLARE_DYNCREATE中将CRuntimeClass classCMyClass定义为static const类型;而在DECLARE_SERIAL中定义为static 类型,DECLARE_DYNCREATE中不需要修改CRuntimeClass中的m_pNextClass成员。
2.在IMPLEMENT_SERIAL中定义了AFX_CLASSINIT _init_CMyClass(RUNTIME_CLASS(CMyClass));这对构建类信息链表极为关键,如果没有这句,CRuntimeClass中的m_pNextClass的根本就毫无用处。
结论:
用DECLARE_DYNCREATE可以确定运行时的类的继承关系,但并没有建立类信息链表。
用IMPLEMENT_SERIAL可以建立运行时类信息链表,有了类信息链表就可以根据字符串在链表中查询出对应的类信息,然后创建对象。
例程下载地址:http://cmmy.vicp.net/bbs/viewFile.asp?Boardid=2&ID=2035