《MFC深入浅出》总结

刚来实习,貌似暂时没有什么任务,闲得很,用的是MFC,前几天好像要用到 CTabCtrl,然后我不会,晕。。 决定恶补一下MFC内容。 三天时间,把<<MFC深入浅出>>翻了一翻,把一些该懂但还不懂的看看,难的略过。总结如下。


一 win32 基本程序观念
call back 函数,意思是指  在你的程序中,被Windows 系统调用.
为什么Windows Programming Modal 要把窗口函数设计为一个call back 函数?为什么不让程序在抓到消息(GetMessage)之后直接调用它就好了?原因是,除了你需要调用它,有很多时候操作系统也要调用你的窗口函数(例如当某个消息产生或某个事件发生)。窗口函数设计为callback 形式,才能开放出一个接口给操作系统叫用。
WM_CLOSE destroy窗口 , 再发送 WM_QUIT 退出消息循环。
WM_CLOSE -> 调用 DestroyWindow -> 发出 WM_DESTROY -> ::PostQuitMessage -> 发出 WM_QUIT -> 结束消息循环。 


二 C++的重要性质
从操作型定义来看,什么是虚拟函数呢?如果你预期衍生类别有可能重新定义某一个成员函数,那么你就在基础类别中把此函数设为virtual。MFC 有两个十分十分重要的虚拟函数:与document 有关的Serialize 函数和与view 有关的OnDraw 函数。你应该在自己的CMyDoc 和CMyView 中改写这两个虚拟函数。
编译器无法在编译时期判断pEmp->computePay到底是调用哪一个函数,必须在执行时期才能评估之,这称为后期绑定late binding 或动态绑定dynamic binding。至于C 函数或C++ 的non-virtual 函数,在编译时期就转换为一个固定地址的调用了,这称为前期绑定early binding 或静态绑定static binding。

多态 适用于一般化操作, 如 for(各种职员)  { 用 基类引用,作一般化操作,注意这里应该是传址哟。 }
编译时多态(重载就能实现了)      运行时多态(虚函数实现)。
实现虚函数,用 虚拟函数表 实现。  每个实例化对象,都有虚拟函数表。。
动态绑定机制,在执行时期,根据虚拟函数表,做出了正确的选择。

构造函数与析构函数:
对于全域对象(如本例之GlobalObject),程序一开始,其构造式就先被执行(比程序进入点更早);程序即将结束前其析构式被执行。MFC 程序就有这样一个全域对象,通常以application object 称呼之,你将在第6章看到它。对于区域对象,当对象诞生时,其构造式被执行;当程序流程将离开该对象的存活范围(以至于对象将毁灭),其析构式被执行。对于静态(static)对象,当对象诞生时其构造式被执行;当程序将结束时(此对象因而将遭致毁灭)其析构式才被执行,但比全域对象的析构式早一步执行。
对于以new 方式产生出来的区域对象,当对象诞生时其构造式被执行。析构式则在对象被delete 时执行(上例程序未示范)。
第四种情况(区域静态对象)相当类似C 语言中的静态区域变量,只会有一个实体(instance)产生,而且在固定的内存上(既不是stack 也不是heap)。它的构造式在控制权第一次移转到其声明处(也就是在MyFunc 第一次被调用)时被调用。

ps.就是出了这个函数后, 出了函数后,变量还存在。 但是只能在函数内部使用- -!。

六 MFC程序的生死因果

Application object是一个全域对象,代表整个程序,其基础类别是CWinApp。MainFrame object,代表主窗口,其基础类别是CFrameWnd。
MFC 就把有着相当固定行为之WinMain 内部动作包装在CWinApp 中,把有着相当固定行为之WndProc 内部动作包装在CFrameWnd 中。CWinApp-取代 WinMain的地位,CFrameWnd-取代 WndProc的地位

程序进入点:AfxWinMain
CWinApp* pApp = AfxGetApp();
pApp->InitApplication();
pApp->InitInstance();
nReturnCode = pApp->Run();

AfxWinMain 的四个主要动作以及引发的行为。
1. AfxWinInit-AFX内部初始化动作。 AfxWinInit 是继CWinApp 构造式之后的第一个动作(全局变量,领先于 WinMain函数 执行).
2. CWinApp::InitApplication    初始化应用程序必须要做的事情,MFC内部管理用,不般不去理它。
3. CMyWinApp::InitInstance      //这个一定要被改写哟,要不然不知道要产生什么窗口。(在这里,必须要调用4,你可以弄一个dlg,然后把它显示出来。。 )
4. CFrameWnd::Create 产生主窗口(并先注册窗口类别)(注册窗口类别,创建窗口,发送WM_PAINT消息)
5. CWinApp::Run - 程序生命的活水源头 () (在这里 GetMessage , TranslateMessage,DispatchMessage)

如果类别的成员函数是一个callback 函数,你必须声明它为"static",才能把C++ 编译器加诸于函数的一个隐藏参数this 去掉。
空闲时间(idle time)的处理:OnIdle。。       消息队列中取不到消息时,就执行这个函数- -!如果你的MFC 程序也想处理idle time,只要改写CWinApp 衍生类别的OnIdle 函数即可。

你永远可以改变MFC 的预设行为。这一点是无庸置疑的,因为所有你可能需要改变的性质,都被设计为MFC 类别中的虚拟函数了,你可以从MFC 衍生出自己的类别,并改写那些虚拟函数。


七. 简单而完整:MFC骨干程序。
我已经多次强调,Document/View 是MFC 进化为Application Framework 的灵魂。
CDocument 简单地说就是负责处理资料的类别。
CView 就是为了资料的表现而设计的。
CMyDoc::Serialize 连载  a.与硬盘交互,可以读写文件。  b.与View交互,可以与View类交换数据。 一个View对应一个Document.
有关文件读写的动作在CDocument 的Serialize 函数进行,有关画面显示的动作在CView 的OnDraw 或OnPaint 函数进行。当我为自己衍生两个类别CMyDoc和CMyView,我只要把全付心思花在CMyDoc::Serialize 和CMyView::OnDraw 身上,其它琐事一概不必管,整个程序自动会运作得好好的.档对象一个文档对象可以和多个视类对象相关联,而一个视类对象只能和一个文档对象相关联。

Document Template 是一个全新的观念。
稍早我已提过Document/View 的概念,它们互为表里。View 本身虽然已经是一个窗口,其外围却必须再包装一个外框窗口做为舞台。这样的切割其实是为了让View 可以非常独立地放置于「MDI Document Frame 窗口」或「SDI Document Frame 窗口」或「OLE Document Frame 窗口」等各种应用之中。也可以说,Document Frame 窗口是View 窗口的一个容器。资料的内容、资料的表象、以及「容纳资料表象之外框窗口」三者是一体的,换言之,程序每打开一份文件(资料),就应该产生三份对象:
1. 一份Document 对象,
2. 一份View 对象,
3. 一份CMDIChildWnd 对象(做为外框窗口)
这三份对象由一个所谓的Document Template 对象来管理。让这三份对象产生关系的关键在于CMultiDocTemplate.


第八章 Document-View深入探讨。
MFC 之所以为Application Framework,最重要的一个特征就是它能够将管理资料的程序码和负责资料显示的程序代码分离开来,这种能力由MFC 的Document/View 提供。
Document 其实就是资料.
Document 在MFC 的CDocument 里头被具体化。CDocument 本身并无实务贡献,它只是提供一个空壳。当你开发自己的程序,应该从CDocument 衍生出一个属于自己的
Document 类别,并且在类别中声明一些成员变量,用以承载(容纳)数据。然后再(至少)改写专门负责文件读写动作的Serialize 函数。
View 在MFC 的CView 里头被具体化。CView 本身亦无实务贡献,它只是提供一个空壳。当你开发自己的程序,应该从CView 衍生出一个属于自己的View 类别,并且在
类别中(至少)改写专门负责显示资料的OnDraw 函数(针对屏幕)或OnPrint 函数(针对打印机)。

MFC 把Document/View/Frame 视为三位一体。可不是吗!每当使用者欲打开(或新增)一份文件,程序应该做出Document、View、Frame 各一份。这个「三口组」成为一个运作单元,由所谓的Document Template 掌管。MFC 有一个CDocTemplate 负责此事。它又有两个衍生类别,分别是CMultiDocTemplate 和CSingleDocTemplate。
CDocTemplate 是个抽象类别,定义了一些用来处理「Document/View/Frame 三口组」的基础函数。

CDocTemplate、CDocument、CView、CFrameWnd 之间的关系。
下面则是
一份文字整理:
■ CWinApp 拥有一个对象指针:CDocManager* m_pDocManager。
■ CDocManager 拥有一个指针串行CPtrList m_templateList,用来维护一系列的Document Template。一个程序若支持两「种」文件类型,就应该有两份Document Templates,应用程序应该在CMyWinApp::InitInstance 中以AddDocTemplate 将这些Document Templates 加入由CDocManager 所维护的串行之中。
■ CDocTemplate 拥有三个成员变量,分别持有Document 、View、Frame 的CRumtimeClass 指针,另有一个成员变量m_nIDResource,用来表示此Document显现时应该采用的UI 对象。这四份资料应该在CMyWinApp::InitInstance 函数构造CDocTemplate时指定之,成为构造式的参数。当使用者欲打开一份文件(通常是借着【File/Open】或【File/New】命令项),CDocTemplate 即可藉由Document/View/Frame 之CRuntimeClass 指针进行动态生成。
■ CDocument 有一个成员变量CDocTemplate* m_pDocTemplate,回指其Document Template;另有一个成员变量CPtrList m_viewList,表示它可以同时维护一系列
的Views。
■ CFrameWnd 有一个成员变量CView* m_pViewActive ,指向目前正作用中的
View 。
■ CView 有一个成员变量CDocument* m_pDocument,指向相关的Document。

CDocument/CView的自我总结:
1. CDocument负责资料整理。
    在 Serialize中,用一个 CArchive ar文档类 进行资料的管理。CArchive很强大呀。。它一定关联着一个CFile.
        a.保存内容。 我们要把内容放进 ar中。这个ar没有名字,但是它自己有关联CFile,不理。
            如果想把 m_obArray里面的内容保存起来,可以 m_obArray.Serialize(ar); 直接保存哟。 否则就 ar<<i慢慢弄。
        b.加载内容。 我们要将 ar关联着的CFile内容,给弄出来。 弄出来之后可以放在 CMyDoc.m_obArray 或者 其它的变量中 数组中。。
            如果想 把内容全部放在 m_obArray中,可以 m_obArray.Serialize(ar)直接写进去。 否则 就 ar>>i慢慢弄。。
2. CView负责显示。显示的内容,一般是 其关联着的CDocument中的 m_obArray 的内容。。在OnDraw中,
    CMyDoc *pDoc = GetMyDocument(); //一个文档对象可以和多个视类对象相关联,而一个视类对象只能和一个文档对象相关联。
    pDoc->m_obArray.get(i).draw();    就可以一个一个画出来咯。  非常地简单。当时竟然没看懂,。
    
菜单(命令)消息有专门路由(oncmdmsg)转发并处理,如果处理完毕则不必转发到下一个.
 <深入浅出MFC>中通过查看源码可以知道:
 消息路径是frame(传递而不处理)->view(如果未处理)-document(如果未处理)--docunment template(如果未处理)--frame本 身(如果未处理)--cwinapp.

其实Framework 是先调用OnPaint,OnPaint 再调用OnDraw。

储存和恢复对象的过程在
MFC 之中就称为serialization。负责这件重要任务的,是MFC CObject 类别中一个名为Serialize 的虚拟函数,文件的「读」「写」动作均透过它。      

一个类别意欲成为Serializable,必须有下列五大条件;至于其原因,前面的讨论已经全部交待过了。
1. 从CObject 衍生下来。如此一来可保有RTTI、Dynamic Creation 等机能。
2. 类别的声明部份必须有DECLARE_SERIAL 宏。此宏需要一个参数:类别名称。
3. 类别的实作部份必须有IMPLEMENT_SERIAL 宏。此宏需要三个参数:一是类别名称,二是父类别名称,三是schema no.。
4. 改写Serialize 虚拟函数,使它能够适当地把类别的成员变量写入文件中。
5. 为此类别加上一个default 构造式(也就是无参数之构造式)。这个条件常为人所忽略,但它是必要的,因为若一个对象来自文件,MFC 必须先动态生成它,而且在没有任何参数的情况下调用其构造式,然后才从文件中读出对象资料。   很多类都满足的。。

CArchive 对象,这一点相信你已经从上面数节讨论中熟悉了。基本上你可以想象archive
相当于文件,不过它其实是文件之前的一个内存缓冲区。 所以构造它的时候,它一定会关联一个文件的。

十四  MFC 多线程程序设计
Windows 系统提供四种同步化机制,帮助程序进行这种工作:
1. Critical Section(关键区域)
2. Semaphore(信号量)
3. Event(事件)
4. Mutex(Mutual Exclusive,互斥器)
我要同步我就优先选择信号量。。原理已经明白,以后要用到再百度搜索一下用法就可以咯。

总结: 三天时间,就把这本书中感觉比较会常用到的梳理了一遍。 查漏补缺还是很有用的。以前许多不理解的,模糊的,忘记了的也有所改善。。





猜你喜欢

转载自blog.csdn.net/a576323437/article/details/9611873