天天看点

windows程序设计(21):剖析单文档架构

之前我们剖析了一个最简单的MFC程序,有了这个基础,我们就可以剖析一个MFC的单文档应用程序了。

大概浏览了一下,我们发现3处不一样(而不是增加)的地方:

1.InitInstance

pDocTemplate = new CSingleDocTemplate(
		IDR_MAINFRAME,
		RUNTIME_CLASS(CSingleDocDoc),
		RUNTIME_CLASS(CMainFrame),       // 主 SDI 框架窗口
		RUNTIME_CLASS(CSingleDocView));
           

跟最基本的:

m_pMainWnd = new CMainWindow;
           

很像。

2.框架类的声明中多了DECLARE_DYNCREATE(CMainFrame)宏

3..框架类的实现中多了IMPLEMENT_DYNCREATE(CMainFrame, CFrameWnd)宏

这是我们这次要讨论的3个重点。

先看第一点,我们将RUNTIME_CLASS宏展开:

pDocTemplate = new CSingleDocTemplate(
		IDR_MAINFRAME,
/*		RUNTIME_CLASS(CTest1Doc),
		RUNTIME_CLASS(CMainFrame),       // main SDI frame window
		RUNTIME_CLASS(CTest1View));
*/
(CRuntimeClass*)(&CTest1Doc::classCTest1Doc),
(CRuntimeClass*)(&CMainFrame::classCMainFrame),
(CRuntimeClass*)(&CTest1View::classCTest1View));
           

明显的,这里调用了CSingleDocTemplate的构造函数。而CRuntimeClass是一个结构体:

struct CRuntimeClass
{
// Attributes
	LPCSTR m_lpszClassName;
	int m_nObjectSize;
	UINT m_wSchema; // schema number of the loaded class
	CObject* (PASCAL* m_pfnCreateObject)(); // NULL => abstract class
#ifdef _AFXDLL
	CRuntimeClass* (PASCAL* m_pfnGetBaseClass)();
#else
	CRuntimeClass* m_pBaseClass;
#endif

// Operations
	CObject* CreateObject();
	BOOL IsDerivedFrom(const CRuntimeClass* pBaseClass) const;

// Implementation
	void Store(CArchive& ar) const;
	static CRuntimeClass* PASCAL Load(CArchive& ar, UINT* pwSchemaNum);

	// CRuntimeClass objects linked together in simple list
	CRuntimeClass* m_pNextClass;       // linked list of registered classes
};
           

可以看出来,第一个是类名,第二个是对象的大小,第三个与串行化有关,第四个函数指针,函数的作用的创建对象,第五个是获取基类的XX,我们暂时看不清楚,但是从返回值来看,估计也是一个基类的CRuntimeClass结构体成员,最后一个是一个next指针,可见,这个结构体会组成一个链表。至于链表是连着什么东西,暂时看不出来。

下面我们看其他的线索:

class CMainFrame : public CFrameWnd
{
	
protected: // create from serialization only
	CMainFrame();
	DECLARE_DYNCREATE(CMainFrame)
           

我们宏展开:

//	DECLARE_DYNCREATE(CMainFrame)

protected: 
	static CRuntimeClass* PASCAL _GetBaseClass(); 
public: 
	static const AFX_DATA CRuntimeClass classCMainFrame; 
	virtual CRuntimeClass* GetRuntimeClass() const; 

	static CObject* PASCAL CreateObject();
           

看到了3个成员函数和1个成员变量。

而在CFrameWnd、甚至是CCmdTarget中,也有这个宏。也就意味着,这些类肯定会包含跟自己名字的CRuntimeClass类型的成员,和3个函数。

在对应的源文件中,将IMPLEMENT_DYNCREATE(CMainFrame, CFrameWnd)宏展开,得到:

//new了一个CMainFrame
CObject* PASCAL CMainFrame::CreateObject()
{
	return new CMainFrame; 
} 

//获取基类的CRuntimeClass结构
CRuntimeClass* PASCAL CMainFrame::_GetBaseClass()
{
	return (CRuntimeClass*)(&CFrameWnd::classCFrameWnd);
}

//对CRuntimeClass结构赋值
AFX_COMDAT const AFX_DATADEF CRuntimeClass CMainFrame::classCMainFrame = 
{ 
	"CMainFrame", sizeof(class CMainFrame), 0xFFFF,CMainFrame::CreateObject, &CMainFrame::_GetBaseClass, NULL 
}; 

//获取CRuntimeClass结构
CRuntimeClass* CMainFrame::GetRuntimeClass() const 
{ 
	return (CRuntimeClass*)(&CMainFrame::classCMainFrame);
} 
           

分别对应于3各成员函数的定义和一个成员变量的赋值。

下面我们画一个图来说明他们之间的关系:

windows程序设计(21):剖析单文档架构

其中的classXXX是每个类都有的数据成员,通过这个结构体,可以获取基类的结构体,也就可以实现了“准虚函数”机制:先在派生类中找,找不到再去基类中找。而这三个类在App中new了一个CSingleDocTemplate,实现了它们之间的挂接。而对于App类,派生自CWinApp,其中的宏是DECLARE_DYNAMIC。我们可以仔细对比一下:

其他3个类中都是:

#define DECLARE_DYNCREATE(class_name) \
	DECLARE_DYNAMIC(class_name) \
	static CObject* PASCAL CreateObject();
           

而App中只有:DECLARE_DYNAMIC,没有CreateObject函数,(之前分析过)这个函数是来new一个对象的。而我们知道MFC中的App类有且只有一个全局对象·theApp,不应该再出现其他的对象,自然就不应该有CreateObject函数了。