深入了解MFC动态创建

先说句客气话,很久没有更新此系列了,这段时间太闲,所以人也太懒
再说句屎话,这几天的股市,真是tmd的狗屎啊
最后再说句屁话,MFC的动态创建,就是一个屁!
CRuntimeClass作为一个很奇怪的存在,在MFC中的地位还很高,但是很多书上都没有说这个东西到底有什么用,还是看看他的代码吧:
struct CRuntimeClass
{
// Attributes
    LPCSTRm_lpszClassName;
    intm_nObjectSize;
    UINTm_wSchema; // schema number of the loaded class
    CObject*(PASCAL* m_pfnCreateObject)(); // NULL => abstractclass
#ifdef _AFXDLL
   CRuntimeClass* (PASCAL* m_pfnGetBaseClass)();
#else
   CRuntimeClass* m_pBaseClass;
#endif

// Operations
    CObject*CreateObject();
    BOOLIsDerivedFrom(const CRuntimeClass* pBaseClass) const;

    //dynamic name lookup and creation
    staticCRuntimeClass* PASCAL FromName(LPCSTR lpszClassName);
    staticCRuntimeClass* PASCAL FromName(LPCWSTR lpszClassName);
    staticCObject* PASCAL CreateObject(LPCSTR lpszClassName);
    staticCObject* PASCAL CreateObject(LPCWSTR lpszClassName);

// Implementation
    voidStore(CArchive& ar) const;
    staticCRuntimeClass* PASCAL Load(CArchive& ar, UINT*pwSchemaNum);

    //CRuntimeClass objects linked together in simple list
   CRuntimeClass*m_pNextClass;      // linked list of registered classes
    constAFX_CLASSINIT* m_pClassInit;
};
由以上声明,可以看出这个结构体有几个作用:
1.记录并提供类信息(注意,是类信息,不是类对象信息)。包括类的名字,大小,schema(这个东西据说是在存文件时用来标志版本信息的)
2.动态创建对象
3.对象存储

虽然1不是本文的重点,还是提一下。C++有一个RTTI的功能,可以找到类的名字。但MFC开发小组开发MFC时,C++语言项目组还没有提供这个功能,所以自己搞了一个RTCI的功能,其核心就是CRuntimeClass。一个与CRuntimeClass绑定的类(注意,是类,不是类对象),可以通过CRuntimeClass,知道自己的类名。
3在序列化中会提到,这里不多说。

具体说2,说说为什么是个屁。
按照我的理解,所谓动态创建,应该满足两个条件:
条件1,创建过程不依赖类声明,可以根据某个标记,或名字,或ID,来创建对象
条件2,使用过程不依赖类声明,引用和保存类对象不需要了解类声明
而这两个条件,MFC都没有做到。CRuntimeClass以类工厂的形式,提供了两种创建对象的方式。但是这两种方式都没有脱离被创建类声明。
先说第一种,通过成员函数CreateObject(非静态)创建对象:
CObject*CRuntimeClass::CreateObject()
{
    return(*m_pfnCreateObject)();
}
此方式需要CRuntimeClass对象。不巧的是,CRuntimeClass对象的声明正好位于其被创建的类中。
例如,创建一个多文档程序,打开文件MainFrm.h,在CMainFrame的声明中,第一句就是DECLARE_DYNAMIC(CMainFrame)。展开这个宏:
protected:
    staticCRuntimeClass* PASCAL _GetBaseClass();
public:
    static constCRuntimeClass classCMainFrame;
    staticCRuntimeClass* PASCAL GetThisClass();
    virtualCRuntimeClass* GetRuntimeClass() const;
静态成员变量classCMainFrame就是类工厂对象。
因此通过这种方式创建对象,首选需要将此头文件include,违背条件1。

第二种方式,通过静态成员函数CreateObject创建对象:
CObject* PASCALCRuntimeClass::CreateObject(LPCSTRlpszClassName)
{
   CRuntimeClass* pClass = FromName(lpszClassName);
    returnpClass == NULL ? NULL :pClass->CreateObject();
}
这种方式只需要类名称,就能创建对象。听起来很美,但调用后你会发现,CreateObject返回的永远是NULL,为什么,因为FromName返回的总是NULL。看看FromName的实现:
CRuntimeClass* PASCALCRuntimeClass::FromName(LPCSTRlpszClassName)
{
   CRuntimeClass* pClass=NULL;
   AFX_MODULE_STATE* pModuleState = AfxGetModuleState();
    for (pClass= pModuleState->m_classList; pClass != NULL; pClass= pClass->m_pNextClass)
   {
       if (lstrcmpA(lpszClassName, pClass->m_lpszClassName)== 0)
       {
           return pClass;
       }
   }
    return NULL;// not found
}
FromName从AFX_MODULE_STATE的m_classList中找的CRuntimeClass对象。如何将CRuntimeClass对象放到这个list中?在CRuntimeClass的声明上面,我找到了这个函数:
void AFXAPI AfxClassInit(CRuntimeClass* pNewClass)
{
   AFX_MODULE_STATE* pModuleState = AfxGetModuleState();
   pModuleState->m_classList.AddHead(pNewClass);
}
如此简单。我马上在CYourApp的InitInstance中添加了如下代码:
AfxClassInit(RUNTIME_CLASS(CYourDoc));
AfxClassInit(RUNTIME_CLASS(CChildFrame));
AfxClassInit(RUNTIME_CLASS(CYourView));
理论上以后无论在哪里,都可以动态这三个类了。编译运行,嘭的一声,出错了:
void CSimpleList::AddHead(void*p)
{
   *GetNextPtr(p) = m_pHead;
}
错误就出在上面这行代码。原因很简单,这句话妄图给CRuntimeClass的m_pClassInit赋值,很不幸,CRuntimeClass对象被声明成了const。
因此,这条路根本就走不通。条件1仍然没有被满足。

其实无论用哪种方式,创建出来的对象都必须用类指针来记住,在MFC中没有接口的概念,因此注定离不开对类声明的引用,此为违背条件2。


你可能感兴趣的:(Module,null,Class,mfc,pascal,attributes)