您的位置:首页 > 其它

深入了解MFC动态创建

2012-06-28 20:37 260 查看
先说句客气话,很久没有更新此系列了,这段时间太闲,所以人也太懒

再说句屎话,这几天的股市,真是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。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: