现在的位置: 首页 > 自动控制 > 工业·编程 > 正文

框架的学习(四):MFC之文档视图结构之诞生

2012-08-27 23:47 工业·编程 ⁄ 共 3642字 ⁄ 字号 暂无评论

    Document/View 结构可以看作是MFC的精华之一。MFC中许多成本都耗在了这个机构上了。

    Document/View与Smalltalk/Stus 中MVC结构是相同的概念。只是各自的实现不同而已。这里只谈MFC。其M=Document,V=view,C=CDocmager + CDocTemplate. 当然这只是简而言之。具体内部是比较复杂的(比如为此MFC内建许多共享结构和宏)。

由于D/V结构是一个比较复杂的机构所以我打算分阶段的学习先学习D/V结构的诞生再学习D/V的运转。在学习的过程中在分析流程的同时分析出所使用的类和类函数再分析在这个过程中MFC类的职责。

    我们先看使用D/V结构诞生, 对于OO程序可以将程序认为是一台舞台剧,一些类或者类数据是布景已开始就存在,一些类是演员需要声明或者实例才能进入舞台。舞台的运转依赖消息的传递。

先看布景:所谓布景在C ++语言中就是指的静态数据结结构。这些数据结构是可以被其他的类实例所共享的。而且其出场时间也最早在定义即必须初始化并且在编译阶段亦决定内存位置。这些数据一般都定义成类的静态数据。在MFC中在为了实现和MFC设计这样的一些静态结构:

AFX_MOUDLE_STATE

这个准确的说不能称之为静态结构,其是用来包存应用程序及线程中重要性质的结构。其是在操作系统调用程序是就布置了。里面对于D/V有用的是 m_ClassList 变量。它指向了后面说的CRunClass 网的头结构。在做动态创建的时候得类名匹配工作将使用它来做最初如入口。

RunTimeClass

RunTimeClass 是为动态创建,和动态识别准备的数据机构。因为C++标准编译器当中是不支持动态创建的。所谓动态创建可以认为是类晚期绑定。一般类名称与类空间需要类的访问权限都已经在编译过程中静态确定下来了。但是如果说需要在程序运行过程根据得到一个类名字符串来实例一个类。这就称之为动态创建或者是类的晚期绑定。

简化点说就是

Char* lpszClassName = “ CTest” ;(其中 “CTest” 是从文件中来的文件名)

CObject* pOb=new class;

所谓动态创建其实就像是实现上面的功能通过字符串new 出类来。但是上面的语句是通不过的。从编译器的角度来说。字符串标示符与类标示符在做绑定的时候内存是两个概念。字符串本身没有new 操作符(这个和字符数组标示符不同)。所以上面的过程通不过。

怎么办MFC 采用的方法就为每个类建一个类标示符对应字符串标示符。例如CTest就建立一个”CTest”再将实例类的操作与这个字符联系起来。这样完成了动态创建。同时也诞生了CRunTimeClass这个结构体。同时为了能将这个结构体以及对应实例类的操作方便的封装到类当中去于是就诞生了三个宏(MFC喜欢用宏)DNYNAMIC(用来声明和填充结构体)DNYCREATE(在上面建立好的基础上补充实例化操作)Seriela(重载<< 操作符)

同时在这个结构体中中还包括了类的基类和下一个机构体的指针。这样结构就形成了。CRuntimeClass 如下

{

m_lpszClassName----- 类名字符串

m_nObjectSize--------- 类大小

m_pBaseClass--------- 基类结构

m_pfnCreateObject---- 实例化操作

m_pNextClass--------- 下一类

m_nShema-------------- 类版本

}

这个结构的存在使得MFC的类可以实现了动态创建。一般而言对于类的实例来说这些是共享的所以这个结构被设置成静态的成了布景。在定义类的时候通过上述的三个宏来定义类的CRunTimeClass。

(在Java 中这些宏被设计成类转载器一个类来完成 这样似乎在设计上更为合理些不过MFC没有这么做)。

pStaticCDocmanger

pStaticList

bStaticInit

这三个静态变量是CDocManager 定义的三个静态变量。分别用来管理当前CDocManger 类实例。以及当前类模版。其中pStaticCDocmanger保证应用程序中只有一个CDocManger实例。pStaticList 得到的是应用程序中的模版列表。 它作用基本上可以为新添的类提供默认的模版。

再看演员

演员和布景不一样演员是由出场时间的。所以我们先讨论演员们的出场时间。和出场过程。

CWinApp 这个就不多说了在程序Main 函数之前就出现了!

CDocManager 出场有两个方式不过都是在CWinApp::IntilInstance() 当中

一种方式是在实例化DocTemplate的时候。在CDocTemplate时候如果CDocManager为空则生成一个实例并将指针保存到pStaticCDocmanger

中去。

另一方式在CWinApp::AddDocTemplate函数中如果m_pDocManger为空的话也会生成一个CDocManager

总之在整个应用程序中都只维护一个CDocManager使用pStaticCDocmanger来维护。

CDcoTemplate 这个在CWinApp::IntilInstanc函数中手工创建

CDocment

CView

CFrame (这里用这三个类泛指文档/视图/窗体类)

所谓D/V结构的与原因之一就是在类的创建过程当中总是一组对应Docment/View/Frame 。这样的一个组的创建过程要分成两种一种new 一个是open 过程。New 过程不涉及到文档数据的读写而open的过程涉及到文档数据的读写。这里先讨论new 过程

New 过程

1 消息先到CWinApp::OnFileNew函数这个函数调用CDocManger的OnFileNew函数

2 CDocManger:: OnFileNew函数负责弹出对话框选择文档类型。根据选择的类型在m_pTemplateList指像的列表中找出对应CDocTemplate

3 调用CDocTemplate 的OpenDocumentFile函数 不过这个时候利用多态性调用的时指向对象的函数。

4 先创建调用CreateNewDocment 来创建CDocment,CreateNewDocment中使用成员变量m_pDocClass->CreateObject() 动态创建类(这里前面讨论CRunTimeClass发挥了作用了)同时将template指针保存给Docment。

5 再调用CreateNewFrame 来创建CFrame 先用m_pDocFrame->CreateObject()得到窗体类。然后和一般窗体常见过程差不多。调用LoadFrame 再调用Create 在调用CreateEx在再在PreCreateWindow 中注册窗体 调用::CreateWindowEx 构造然后结束。

6 最后对于CView的创建要复杂一些,在创建窗体的时候会塞入一个CCreateContext的东西里面有一个m_pViewClass的指针。在窗体创建以后会发出ON_WM_CREATE消息。同时将一个CREATESTRUCT类型的结构体给消息处理函数。而这个结构体的最后一个参数就是CCreateContext结构。这个结构的最后一个结构就是m_pViewClass 然后接着就是调用OnCreateHelper,OnCreateClient,CreateView函数最后在CreateView中动态创建类同时创建View窗体。过程如下

上面的讨论以及讨论了基本上D/V的重要类和其诞生状况。

这样我们来理一下

所有的背景数据结构

AFX_MOUDLE_STATE 职责就是标示类的状态

RunTimeClass 职责就是为动态创建和写入读出做准备,

pStaticCDocmanger 职责之维护一个CDocManger

pStaticList

bStaticInit

创建数据结构()

CWinApp 其职责在D/V结构有两点为创建D/V结构提供场所二将文档创建/打开消息收集起来给D/V 结构

前者的场所在Instance函数后者通过在OnNewFile/OnOpenFile 调用CDocmanger 对应的函数

CDocManger 这个类有的数据是指向template 链表的指针的数据。所以这个类的职责是获取需要的Template名然后比较得到得到template体

,CDocTemplate 这个类的数据是三个CRunTimeClass的指针那么就意味着CDocTemplat职责就是动态创建这三个类并且建立这个三个类的关系。

CDocment类的主要职责是管理数据

CView类主要职责是显示。

这里对于D/V结构诞生时各个类的职责已经比较清晰了。但是在D/V结构运转时各类的职责还不是比较清晰这里D/V运转主要包括文档的读写,文档和视图的同步更新等。特别是CDOCMENT/CView 类的职责。 不过可能会分成几个部分来学习

给我留言

留言无头像?