深入浅出MFC“文档/视图”架构(2)――文档模板

简介:
深入浅出MFC“文档/视图”架构(2)

――文档模板

作者:宋宝华  e-mail:21cnbao@21cn.com

1.文档模板管理者类CDocManager

在“文档/视图”架构的MFC程序中,提供了文档模板管理者类CDocManager,由它管理应用程序所包含的文档模板。我们先看看这个类的声明:

/////////////////////////////////////////////////////////////////////////////

// CDocTemplate manager object

 

class CDocManager : public CObject

{

       DECLARE_DYNAMIC(CDocManager)

public:

 

// Constructor

       CDocManager();

 

       //Document functions

       virtual void AddDocTemplate(CDocTemplate* pTemplate);

       virtual POSITION GetFirstDocTemplatePosition() const;

       virtual CDocTemplate* GetNextDocTemplate(POSITION& pos) const;

       virtual void RegisterShellFileTypes(BOOL bCompat);

       void UnregisterShellFileTypes();

       virtual CDocument* OpenDocumentFile(LPCTSTR lpszFileName); // open named file

       virtual BOOL SaveAllModified(); // save before exit

       virtual void CloseAllDocuments(BOOL bEndSession); // close documents before exiting

       virtual int GetOpenDocumentCount();

 

       // helper for standard commdlg dialogs

       virtual BOOL DoPromptFileName(CString& fileName, UINT nIDSTitle,

                     DWORD lFlags, BOOL bOpenFileDialog, CDocTemplate* pTemplate);

 

//Commands

       // Advanced: process async DDE request

       virtual BOOL OnDDECommand(LPTSTR lpszCommand);

       virtual void OnFileNew();

       virtual void OnFileOpen();

 

// Implementation

protected:

       CPtrList m_templateList;

       int GetDocumentCount(); // helper to count number of total documents

 

public:

       static CPtrList* pStaticList;       // for static CDocTemplate objects

       static BOOL bStaticInit;            // TRUE during static initialization

       static CDocManager* pStaticDocManager;  // for static CDocTemplate objects

 

public:

       virtual ~CDocManager();

#ifdef _DEBUG

       virtual void AssertValid() const;

       virtual void Dump(CDumpContext& dc) const;

#endif

};

从上述代码可以看出,CDocManager类维护一个CPtrList类型的链表m_templateList(即文档模板链表,实际上,MFC的设计者在MFC的实现中大量使用了链表这种数据结构),CPtrList类型定义为:

class CPtrList : public CObject

{

       DECLARE_DYNAMIC(CPtrList)

 

protected:

       struct CNode

       {

              CNode* pNext;

              CNode* pPrev;

              void* data;

       };

public:

 

// Construction

       CPtrList(int nBlockSize = 10);

 

// Attributes (head and tail)

       // count of elements

       int GetCount() const;

       BOOL IsEmpty() const;

 

       // peek at head or tail

       void*& GetHead();

       void* GetHead() const;

       void*& GetTail();

       void* GetTail() const;

 

// Operations

       // get head or tail (and remove it) - don't call on empty list!

       void* RemoveHead();

       void* RemoveTail();

 

       // add before head or after tail

       POSITION AddHead(void* newElement);

       POSITION AddTail(void* newElement);

 

 

       // add another list of elements before head or after tail

       void AddHead(CPtrList* pNewList);

       void AddTail(CPtrList* pNewList);

 

       // remove all elements

       void RemoveAll();

 

       // iteration

       POSITION GetHeadPosition() const;

       POSITION GetTailPosition() const;

       void*& GetNext(POSITION& rPosition); // return *Position++

       void* GetNext(POSITION& rPosition) const; // return *Position++

       void*& GetPrev(POSITION& rPosition); // return *Position--

       void* GetPrev(POSITION& rPosition) const; // return *Position--

 

       // getting/modifying an element at a given position

       void*& GetAt(POSITION position);

       void* GetAt(POSITION position) const;

       void SetAt(POSITION pos, void* newElement);

 

       void RemoveAt(POSITION position);

 

       // inserting before or after a given position

       POSITION InsertBefore(POSITION position, void* newElement);

       POSITION InsertAfter(POSITION position, void* newElement);

 

 

       // helper functions (note: O(n) speed)

       POSITION Find(void* searchValue, POSITION startAfter = NULL) const;

                                          // defaults to starting at the HEAD

                                          // return NULL if not found

       POSITION FindIndex(int nIndex) const;

                                          // get the 'nIndex'th element (may return NULL)

 

// Implementation

protected:

       CNode* m_pNodeHead;

       CNode* m_pNodeTail;

       int m_nCount;

       CNode* m_pNodeFree;

       struct CPlex* m_pBlocks;

       int m_nBlockSize;

 

       CNode* NewNode(CNode*, CNode*);

       void FreeNode(CNode*);

 

public:

       ~CPtrList();

#ifdef _DEBUG

       void Dump(CDumpContext&) const;

       void AssertValid() const;

#endif

       // local typedefs for class templates

       typedef void* BASE_TYPE;

       typedef void* BASE_ARG_TYPE;

};

很显然,CPtrList是对链表结构体

struct CNode

{

       CNode* pNext;

       CNode* pPrev;

       void* data;

};

本身及其GetNext、GetPrev、GetAt、SetAt、RemoveAt、InsertBefore、InsertAfter、Find、FindIndex等各种操作的封装。

作为一个抽象的链表类型,CPtrList并未定义其中节点的具体类型,而以一个void指针(struct CNode 中的void* data)巧妙地实现了链表节点成员具体类型的“模板”化。很显然,在Visual C++6.0开发的年代,C++语言所具有的语法特征“模板”仍然没有得到广泛的应用。

而CDocManager类的成员函数

virtual void AddDocTemplate(CDocTemplate* pTemplate);

virtual POSITION GetFirstDocTemplatePosition() const;

virtual CDocTemplate* GetNextDocTemplate(POSITION& pos) const;

则完成对m_TemplateList链表的添加及遍历操作的封装,我们来看看这三个函数的源代码:

void CDocManager::AddDocTemplate(CDocTemplate* pTemplate)

{

       if (pTemplate == NULL)

       {

              if (pStaticList != NULL)

              {

                     POSITION pos = pStaticList->GetHeadPosition();

                     while (pos != NULL)

                     {

                            CDocTemplate* pTemplate =

                                   (CDocTemplate*)pStaticList->GetNext(pos);

                            AddDocTemplate(pTemplate);

                     }

                     delete pStaticList;

                     pStaticList = NULL;

              }

              bStaticInit = FALSE;

       }

       else

       {

              ASSERT_VALID(pTemplate);

              ASSERT(m_templateList.Find(pTemplate, NULL) == NULL);// must not be in list

              pTemplate->LoadTemplate();

              m_templateList.AddTail(pTemplate);

       }

}

POSITION CDocManager::GetFirstDocTemplatePosition() const

{

       return m_templateList.GetHeadPosition();

}

CDocTemplate* CDocManager::GetNextDocTemplate(POSITION& pos) const

{

       return (CDocTemplate*)m_templateList.GetNext(pos);

}

2.文档模板类CDocTemplate

文档模板类CDocTemplate是一个抽象基类(这意味着不能直接用它来定义对象而必须用它的派生类),它定义了文档模板的基本处理函数接口。对一个单文档界面程序,需使用单文档模板类CSingleDocTemplate,而对于一个多文档界面程序,需使用多文档模板类CMultipleDocTemplate。我们首先来看看CDocTemplate类的声明:

class CDocTemplate : public CCmdTarget

{

       DECLARE_DYNAMIC(CDocTemplate)

 

// Constructors

protected:

       CDocTemplate(UINT nIDResource, CRuntimeClass* pDocClass,

              CRuntimeClass* pFrameClass, CRuntimeClass* pViewClass);

 

public:

       virtual void LoadTemplate();

 

// Attributes

public:

       // setup for OLE containers

       void SetContainerInfo(UINT nIDOleInPlaceContainer);

 

       // setup for OLE servers

       void SetServerInfo(UINT nIDOleEmbedding, UINT nIDOleInPlaceServer = 0,

              CRuntimeClass* pOleFrameClass = NULL, CRuntimeClass* pOleViewClass = NULL);

 

       // iterating over open documents

       virtual POSITION GetFirstDocPosition() const = 0;

       virtual CDocument* GetNextDoc(POSITION& rPos) const = 0;

 

// Operations

public:

       virtual void AddDocument(CDocument* pDoc);      // must override

       virtual void RemoveDocument(CDocument* pDoc);   // must override

 

       enum DocStringIndex

       {

              windowTitle,        // default window title

              docName,            // user visible name for default document

              fileNewName,        // user visible name for FileNew

              // for file based documents:

              filterName,         // user visible name for FileOpen

              filterExt,          // user visible extension for FileOpen

              // for file based documents with Shell open support:

              regFileTypeId,      // REGEDIT visible registered file type identifier

              regFileTypeName,    // Shell visible registered file type name

       };

       virtual BOOL GetDocString(CString& rString,

              enum DocStringIndex index) const; // get one of the info strings

       CFrameWnd* CreateOleFrame(CWnd* pParentWnd, CDocument* pDoc,

              BOOL bCreateView);

 

// Overridables

public:

       enum Confidence

       {

              noAttempt,

              maybeAttemptForeign,

              maybeAttemptNative,

              yesAttemptForeign,

              yesAttemptNative,

              yesAlreadyOpen

       };

       virtual Confidence MatchDocType(LPCTSTR lpszPathName,

                                   CDocument*& rpDocMatch);

       virtual CDocument* CreateNewDocument();

       virtual CFrameWnd* CreateNewFrame(CDocument* pDoc, CFrameWnd* pOther);

       virtual void InitialUpdateFrame(CFrameWnd* pFrame, CDocument* pDoc,

              BOOL bMakeVisible = TRUE);

       virtual BOOL SaveAllModified();     // for all documents

       virtual void CloseAllDocuments(BOOL bEndSession);

       virtual CDocument* OpenDocumentFile(

              LPCTSTR lpszPathName, BOOL bMakeVisible = TRUE) = 0;

                                   // open named file

                                   // if lpszPathName == NULL => create new file with this type

       virtual void SetDefaultTitle(CDocument* pDocument) = 0;

 

// Implementation

public:

       BOOL m_bAutoDelete;

       virtual ~CDocTemplate();

 

       // back pointer to OLE or other server (NULL if none or disabled)

       CObject* m_pAttachedFactory;

 

       // menu & accelerator resources for in-place container

       HMENU m_hMenuInPlace;

       HACCEL m_hAccelInPlace;

 

       // menu & accelerator resource for server editing embedding

       HMENU m_hMenuEmbedding;

       HACCEL m_hAccelEmbedding;

 

       // menu & accelerator resource for server editing in-place

       HMENU m_hMenuInPlaceServer;

       HACCEL m_hAccelInPlaceServer;

 

#ifdef _DEBUG

       virtual void Dump(CDumpContext&) const;

       virtual void AssertValid() const;

#endif

       virtual void OnIdle();             // for all documents

       virtual BOOL OnCmdMsg(UINT nID, int nCode, void* pExtra,

              AFX_CMDHANDLERINFO* pHandlerInfo);

 

protected:

       UINT m_nIDResource;                 // IDR_ for frame/menu/accel as well

       UINT m_nIDServerResource;           // IDR_ for OLE inplace frame/menu/accel

       UINT m_nIDEmbeddingResource;        // IDR_ for OLE open frame/menu/accel

       UINT m_nIDContainerResource;        // IDR_ for container frame/menu/accel

 

       CRuntimeClass* m_pDocClass;         // class for creating new documents

       CRuntimeClass* m_pFrameClass;       // class for creating new frames

       CRuntimeClass* m_pViewClass;        // class for creating new views

       CRuntimeClass* m_pOleFrameClass;    // class for creating in-place frame

       CRuntimeClass* m_pOleViewClass;     // class for creating in-place view

 

       CString m_strDocStrings;    // '\n' separated names

              // The document names sub-strings are represented as _one_ string:

              // windowTitle\ndocName\n ... (see DocStringIndex enum)

};

文档模板挂接了后面要介绍的文档、视图和框架窗口,使得它们得以互相关联。通过文档模板,程序确定了创建或打开一个文档时,以什么样的视图和框架窗口来显示。文档模板依靠保存相互对应的文档、视图和框架窗口的CRuntimeClass对象指针来实现上述挂接,这就是文档模板类中的成员变量m_pDocClass、m_pFrameClass、m_pViewClass的由来。实际上,对m_pDocClass、m_pFrameClass、m_pViewClass的赋值在CDocTemplate类的构造函数中实施:

CDocTemplate::CDocTemplate(UINT nIDResource, CRuntimeClass* pDocClass,

       CRuntimeClass* pFrameClass, CRuntimeClass* pViewClass)

{

       ASSERT_VALID_IDR(nIDResource);

       ASSERT(pDocClass == NULL ||

              pDocClass->IsDerivedFrom(RUNTIME_CLASS(CDocument)));

       ASSERT(pFrameClass == NULL ||

              pFrameClass->IsDerivedFrom(RUNTIME_CLASS(CFrameWnd)));

       ASSERT(pViewClass == NULL ||

              pViewClass->IsDerivedFrom(RUNTIME_CLASS(CView)));

 

       m_nIDResource = nIDResource;

       m_nIDServerResource = NULL;

       m_nIDEmbeddingResource = NULL;

       m_nIDContainerResource = NULL;

 

       m_pDocClass = pDocClass;

       m_pFrameClass = pFrameClass;

       m_pViewClass = pViewClass;

       m_pOleFrameClass = NULL;

       m_pOleViewClass = NULL;

 

       m_pAttachedFactory = NULL;

       m_hMenuInPlace = NULL;

       m_hAccelInPlace = NULL;

       m_hMenuEmbedding = NULL;

       m_hAccelEmbedding = NULL;

       m_hMenuInPlaceServer = NULL;

       m_hAccelInPlaceServer = NULL;

 

       // add to pStaticList if constructed as static instead of on heap

       if (CDocManager::bStaticInit)

       {

              m_bAutoDelete = FALSE;

              if (CDocManager::pStaticList == NULL)

                     CDocManager::pStaticList = new CPtrList;

              if (CDocManager::pStaticDocManager == NULL)

                     CDocManager::pStaticDocManager = new CDocManager;

              CDocManager::pStaticList->AddTail(this);

       }

       else

       {

              m_bAutoDelete = TRUE;   // usually allocated on the heap

              LoadTemplate();

       }

}

文档模板类CDocTemplate还保存了它所支持的全部文档类的信息,包括所支持文档的文件扩展名、文档在框架窗口中的名字、图标等。

CDocTemplate类的AddDocument、RemoveDocument成员函数使得CDocument* pDoc参数所指向的文档归属于本文档模板(通过将this指针赋值给pDoc所指向CDocument对象的m_pDocTemplate成员变量)或脱离与本文档模板的关系:

void CDocTemplate::AddDocument(CDocument* pDoc)

{

       ASSERT_VALID(pDoc);

       ASSERT(pDoc->m_pDocTemplate == NULL);   // no template attached yet

       pDoc->m_pDocTemplate = this;

}

void CDocTemplate::RemoveDocument(CDocument* pDoc)

{

       ASSERT_VALID(pDoc);

       ASSERT(pDoc->m_pDocTemplate == this);   // must be attached to us

       pDoc->m_pDocTemplate = NULL;

}

而CDocTemplate类的CreateNewDocument成员函数则首先调用CDocument运行时类的CreateObject函数创建一个CDocument对象,再调用AddDocument成员函数将其归属于本文档模板类:

CDocument* CDocTemplate::CreateNewDocument()

{

       // default implementation constructs one from CRuntimeClass

       if (m_pDocClass == NULL)

       {

              TRACE0("Error: you must override CDocTemplate::CreateNewDocument.\n");

              ASSERT(FALSE);

              return NULL;

       }

       CDocument* pDocument = (CDocument*)m_pDocClass->CreateObject();

       if (pDocument == NULL)

       {

              TRACE1("Warning: Dynamic create of document type %hs failed.\n",

                     m_pDocClass->m_lpszClassName);

              return NULL;

       }

       ASSERT_KINDOF(CDocument, pDocument);

       AddDocument(pDocument);

       return pDocument;

}

文档类对象由文档模板类构造生成,单文档模板类CSingleDocTemplate只能生成一个文档类对象,并用成员变量 m_pOnlyDoc 指向该对象;多文档模板类可以生成多个文档类对象,用成员变量 m_docList 指向文档对象组成的链表。

CSingleDocTemplate的构造函数、AddDocument及RemoveDocument成员函数都在CDocTemplate类相应函数的基础上增加了对m_pOnlyDoc指针的处理:

CSingleDocTemplate::CSingleDocTemplate(UINT nIDResource,

       CRuntimeClass* pDocClass, CRuntimeClass* pFrameClass,

       CRuntimeClass* pViewClass)

              : CDocTemplate(nIDResource, pDocClass, pFrameClass, pViewClass)

{

       m_pOnlyDoc = NULL;

}

void CSingleDocTemplate::AddDocument(CDocument* pDoc)

{

       ASSERT(m_pOnlyDoc == NULL);     // one at a time please

       ASSERT_VALID(pDoc);

 

       CDocTemplate::AddDocument(pDoc);

       m_pOnlyDoc = pDoc;

}

void CSingleDocTemplate::RemoveDocument(CDocument* pDoc)

{

       ASSERT(m_pOnlyDoc == pDoc);     // must be this one

       ASSERT_VALID(pDoc);

 

       CDocTemplate::RemoveDocument(pDoc);

       m_pOnlyDoc = NULL;

}

同样,CMultiDocTemplate类的相关函数也需要对m_docList所指向的链表进行操作(实际上AddDocument和RemoveDocument成员函数是文档模板管理其所包含文档的函数):

// CMultiDocTemplate document management (a list of currently open documents)

void CMultiDocTemplate::AddDocument(CDocument* pDoc)

{

       ASSERT_VALID(pDoc);

 

       CDocTemplate::AddDocument(pDoc);

       ASSERT(m_docList.Find(pDoc, NULL) == NULL); // must not be in list

       m_docList.AddTail(pDoc);

}

void CMultiDocTemplate::RemoveDocument(CDocument* pDoc)

{

       ASSERT_VALID(pDoc);

 

       CDocTemplate::RemoveDocument(pDoc);

       m_docList.RemoveAt(m_docList.Find(pDoc));

}

由于CMultiDocTemplate类可包含多个文档,依靠其成员函数GetFirstDocPosition和GetNextDoc完成对文档链表m_docList的遍历:

POSITION CMultiDocTemplate::GetFirstDocPosition() const

{

       return m_docList.GetHeadPosition();

}

CDocument* CMultiDocTemplate::GetNextDoc(POSITION& rPos) const

{

       return (CDocument*)m_docList.GetNext(rPos);

}

而CSingleDocTemplate的这两个函数实际上并无太大的意义,仅仅是MFC要玩的某种“招数”,这个“招数”高明吗?相信看完MFC的相关源代码后你或许不会这么认为,实际上CSingleDocTemplate的GetFirstDocPosition、GetNextDoc函数仅仅只能判断m_pOnlyDoc的是否为NULL:

POSITION CSingleDocTemplate::GetFirstDocPosition() const

{

       return (m_pOnlyDoc == NULL) ? NULL : BEFORE_START_POSITION;

}

 

CDocument* CSingleDocTemplate::GetNextDoc(POSITION& rPos) const

{

       CDocument* pDoc = NULL;

       if (rPos == BEFORE_START_POSITION)

       {

              // first time through, return a real document

              ASSERT(m_pOnlyDoc != NULL);

              pDoc = m_pOnlyDoc;

       }

       rPos = NULL;        // no more

       return pDoc;

}

笔者认为,MFC的设计者们将GetFirstDocPosition、GetNextDoc作为基类CDocTemplate的成员函数是不合理的,一种更好的做法是将GetFirstDocPosition、GetNextDoc移至CMultiDocTemplate派生类。

CDocTemplate还需完成对其对应文档的关闭与保存操作:

BOOL CDocTemplate::SaveAllModified()

{

       POSITION pos = GetFirstDocPosition();

       while (pos != NULL)

       {

              CDocument* pDoc = GetNextDoc(pos);

              if (!pDoc->SaveModified())

                     return FALSE;

       }

       return TRUE;

}

void CDocTemplate::CloseAllDocuments(BOOL)

{

       POSITION pos = GetFirstDocPosition();

       while (pos != NULL)

       {

              CDocument* pDoc = GetNextDoc(pos);

              pDoc->OnCloseDocument();

       }

}

前文我们提到,由于MFC的设计者将CSingleDocTemplate和CMultiDocTemplate的行为未进行规范的区分,它对仅仅对应一个文档的CSingleDocTemplate也提供了所谓的GetFirstDocPosition、GetNextDoc遍历操作,所以基类CDocTemplate的SaveAllModified和CloseAllDocuments函数(都是遍历)就可统一CSingleDocTemplate和CMultiDocTemplate两个本身并不相同类的SaveAllModified和CloseAllDocuments行为(实际上,对于CSingleDocTemplate而言,SaveAllModified和CloseAllDocuments中的“All”是没有太大意义的。教室里有1个老师和N个同学,老师可以对同学们说“所有同学”,而学生对老师说“所有老师”相信会被当成神经病)。MFC的设计者们特意使用了“将错就错”的方法意图简化CSingleDocTemplate和CMultiDocTemplate类的设计,读者朋友可以不认同他们的做法。

CDocTemplate还提供了框架窗口的创建和初始化函数:

/////////////////////////////////////////////////////////////////////////////

// Default frame creation

CFrameWnd* CDocTemplate::CreateNewFrame(CDocument* pDoc, CFrameWnd* pOther)

{

       if (pDoc != NULL)

              ASSERT_VALID(pDoc);

       // create a frame wired to the specified document

 

       ASSERT(m_nIDResource != 0); // must have a resource ID to load from

       CCreateContext context;

       context.m_pCurrentFrame = pOther;

       context.m_pCurrentDoc = pDoc;

       context.m_pNewViewClass = m_pViewClass;

       context.m_pNewDocTemplate = this;

 

       if (m_pFrameClass == NULL)

       {

              TRACE0("Error: you must override CDocTemplate::CreateNewFrame.\n");

              ASSERT(FALSE);

              return NULL;

       }

       CFrameWnd* pFrame = (CFrameWnd*)m_pFrameClass->CreateObject();

       if (pFrame == NULL)

       {

              TRACE1("Warning: Dynamic create of frame %hs failed.\n",

                     m_pFrameClass->m_lpszClassName);

              return NULL;

       }

       ASSERT_KINDOF(CFrameWnd, pFrame);

 

       if (context.m_pNewViewClass == NULL)

              TRACE0("Warning: creating frame with no default view.\n");

 

       // create new from resource

       if (!pFrame->LoadFrame(m_nIDResource,

                     WS_OVERLAPPEDWINDOW | FWS_ADDTOTITLE,   // default frame styles

                     NULL, &context))

       {

              TRACE0("Warning: CDocTemplate couldn't create a frame.\n");

              // frame will be deleted in PostNcDestroy cleanup

              return NULL;

       }

 

       // it worked !

       return pFrame;

}

void CDocTemplate::InitialUpdateFrame(CFrameWnd* pFrame, CDocument* pDoc,

       BOOL bMakeVisible)

{

       // just delagate to implementation in CFrameWnd

       pFrame->InitialUpdateFrame(pDoc, bMakeVisible);

}

3. CWinApp与CDocManager/CDocTemplate类

应用程序CWinApp类对象与CDocManager和CDocTemplate类的关系是:CWinApp对象中包含一个CDocManager指针类型的共有数据成员m_pDocManager,CWinApp::InitInstance函数调用CWinApp::AddDocTemplate函数向链表m_templateList添加模板指针(实际上是调用前文所述CDocManager的AddDocTemplate函数)。另外,CWinApp也提供了GetFirstDocTemplatePosition和GetNextDocTemplate函数实现来对m_templateList链表进行访问(实际上也是调用了前文所述CDocManager的GetFirstDocTemplatePosition、GetNextDocTemplate函数)。我们仅摘取CWinApp类声明的一小部分:

class CWinApp : public CWinThread

{



       CDocManager* m_pDocManager;

 

// Running Operations - to be done on a running application

       // Dealing with document templates

       void AddDocTemplate(CDocTemplate* pTemplate);

       POSITION GetFirstDocTemplatePosition() const;

       CDocTemplate* GetNextDocTemplate(POSITION& pos) const;

 

       // Dealing with files

       virtual CDocument* OpenDocumentFile(LPCTSTR lpszFileName); // open named file

       void CloseAllDocuments(BOOL bEndSession); // close documents before exiting

 

// Command Handlers

protected:

       // map to the following for file new/open

       afx_msg void OnFileNew();

       afx_msg void OnFileOpen();

       int GetOpenDocumentCount();



};

来看CWinApp派生类CSDIExampleApp(单文档)、CMDIExampleApp(多文档)的InitInstance成员函数的例子(仅仅摘取与文档模板相关的部分):

BOOL CSDIExampleApp::InitInstance()

{



       CSingleDocTemplate* pDocTemplate;

       pDocTemplate = new CSingleDocTemplate(

              IDR_MAINFRAME,

              RUNTIME_CLASS(CSDIExampleDoc),

              RUNTIME_CLASS(CMainFrame),       // main SDI frame window

              RUNTIME_CLASS(CSDIExampleView));

       AddDocTemplate(pDocTemplate);



       return TRUE;

}

BOOL CMDIExampleApp::InitInstance()

{



       CMultiDocTemplate* pDocTemplate;

       pDocTemplate = new CMultiDocTemplate(

              IDR_MDIEXATYPE,

              RUNTIME_CLASS(CMDIExampleDoc),

              RUNTIME_CLASS(CChildFrame), // custom MDI child frame

              RUNTIME_CLASS(CMDIExampleView));

       AddDocTemplate(pDocTemplate);



}

读者朋友,看完本次连载,也许您有许多不明白的地方,这是正常的。因为其所讲解的内容与后续几次连载息息相关,我们愈往后看,就会愈加清晰。对于本次连载的内容,您只需要建立基本的印象。最初的浅尝辄止是为了最终的深入脊髓!

我们试图对MFC的深层机理刨根究底,“拨开云雾见月明”的过程是艰辛的 





 本文转自 21cnbao 51CTO博客,原文链接:http://blog.51cto.com/21cnbao/120813,如需转载请自行联系原作者





相关文章
|
1月前
|
SQL NoSQL 前端开发
基于BS架构的饰品购物平台设计与实现(程序+文档+数据库)
基于BS架构的饰品购物平台设计与实现(程序+文档+数据库)
|
1月前
|
存储 设计模式 前端开发
请解释 Web 应用程序的 MVC(模型-视图-控制器)架构。
【2月更文挑战第26天】【2月更文挑战第89篇】请解释 Web 应用程序的 MVC(模型-视图-控制器)架构。
|
2月前
MFC应用程序对话框架构
MFC应用程序对话框架构
13 0
|
2月前
MFC单文档视图架构
MFC单文档视图架构
14 0
|
2月前
|
JSON 前端开发 Java
SpringMVC的架构有什么优势?——视图与模型(二)
SpringMVC的架构有什么优势?——视图与模型(二)
|
4月前
|
开发框架 架构师 Java
Java程序员不掌握SpringBoot怎么进大厂,阿里架构师推荐实战文档
Spring Boot作为Java编程语言的一个全新开发框架,在国内外才刚刚兴起时,还未得到普及使用。
|
4月前
|
安全 Java 应用服务中间件
全网最新架构实战文档:高并发+分布式+微服务+SpringBoot+Nginx
关于一线互联网大厂网站的一些特点:用户多,分布广泛、大流量,高并发、海量数据,服务高可用、安全环境恶劣,易受网络攻击、功能多,变更快,频繁发布、从小到大,渐进发展、以用户为中心。
|
4天前
|
敏捷开发 监控 数据管理
构建高效微服务架构的五大关键策略
【4月更文挑战第20天】在当今软件开发领域,微服务架构已经成为一种流行的设计模式,它允许开发团队以灵活、可扩展的方式构建应用程序。本文将探讨构建高效微服务架构的五大关键策略,包括服务划分、通信机制、数据管理、安全性考虑以及监控与日志。这些策略对于确保系统的可靠性、可维护性和性能至关重要。
|
16天前
|
API 数据库 开发者
构建高效可靠的微服务架构:后端开发的新范式
【4月更文挑战第8天】 随着现代软件开发的复杂性日益增加,传统的单体应用架构面临着可扩展性、维护性和敏捷性的挑战。为了解决这些问题,微服务架构应运而生,并迅速成为后端开发领域的一股清流。本文将深入探讨微服务架构的设计原则、实施策略及其带来的优势与挑战,为后端开发者提供一种全新视角,以实现更加灵活、高效和稳定的系统构建。
20 0
|
4天前
|
消息中间件 监控 持续交付
构建高效微服务架构:后端开发的进阶之路
【4月更文挑战第20天】 随着现代软件开发的复杂性日益增加,传统的单体应用已难以满足快速迭代和灵活部署的需求。微服务架构作为一种新兴的分布式系统设计方式,以其独立部署、易于扩展和维护的特点,成为解决这一问题的关键。本文将深入探讨微服务的核心概念、设计原则以及在后端开发实践中如何构建一个高效的微服务架构。我们将从服务划分、通信机制、数据一致性、服务发现与注册等方面入手,提供一系列实用的策略和建议,帮助开发者优化后端系统的性能和可维护性。