《深入浅出mfc》之MFC六大关键技术剖析文档格式.docx

上传人:b****1 文档编号:356792 上传时间:2023-04-28 格式:DOCX 页数:39 大小:41.51KB
下载 相关 举报
《深入浅出mfc》之MFC六大关键技术剖析文档格式.docx_第1页
第1页 / 共39页
《深入浅出mfc》之MFC六大关键技术剖析文档格式.docx_第2页
第2页 / 共39页
《深入浅出mfc》之MFC六大关键技术剖析文档格式.docx_第3页
第3页 / 共39页
《深入浅出mfc》之MFC六大关键技术剖析文档格式.docx_第4页
第4页 / 共39页
《深入浅出mfc》之MFC六大关键技术剖析文档格式.docx_第5页
第5页 / 共39页
《深入浅出mfc》之MFC六大关键技术剖析文档格式.docx_第6页
第6页 / 共39页
《深入浅出mfc》之MFC六大关键技术剖析文档格式.docx_第7页
第7页 / 共39页
《深入浅出mfc》之MFC六大关键技术剖析文档格式.docx_第8页
第8页 / 共39页
《深入浅出mfc》之MFC六大关键技术剖析文档格式.docx_第9页
第9页 / 共39页
《深入浅出mfc》之MFC六大关键技术剖析文档格式.docx_第10页
第10页 / 共39页
《深入浅出mfc》之MFC六大关键技术剖析文档格式.docx_第11页
第11页 / 共39页
《深入浅出mfc》之MFC六大关键技术剖析文档格式.docx_第12页
第12页 / 共39页
《深入浅出mfc》之MFC六大关键技术剖析文档格式.docx_第13页
第13页 / 共39页
《深入浅出mfc》之MFC六大关键技术剖析文档格式.docx_第14页
第14页 / 共39页
《深入浅出mfc》之MFC六大关键技术剖析文档格式.docx_第15页
第15页 / 共39页
《深入浅出mfc》之MFC六大关键技术剖析文档格式.docx_第16页
第16页 / 共39页
《深入浅出mfc》之MFC六大关键技术剖析文档格式.docx_第17页
第17页 / 共39页
《深入浅出mfc》之MFC六大关键技术剖析文档格式.docx_第18页
第18页 / 共39页
《深入浅出mfc》之MFC六大关键技术剖析文档格式.docx_第19页
第19页 / 共39页
《深入浅出mfc》之MFC六大关键技术剖析文档格式.docx_第20页
第20页 / 共39页
亲,该文档总共39页,到这儿已超出免费预览范围,如果喜欢就下载吧!
下载资源
资源描述

《深入浅出mfc》之MFC六大关键技术剖析文档格式.docx

《《深入浅出mfc》之MFC六大关键技术剖析文档格式.docx》由会员分享,可在线阅读,更多相关《《深入浅出mfc》之MFC六大关键技术剖析文档格式.docx(39页珍藏版)》请在冰点文库上搜索。

《深入浅出mfc》之MFC六大关键技术剖析文档格式.docx

//显示框架

returntrue;

//返回

}

};

MyApptheApp;

//①建立应用程序。

设定链接MFC库,运行,即可看见一个窗口。

从上面,大家可以看到建立一个MFC窗口很容易,只用两步:

一是从CWinApp派生一个应用程序类(这里是MyApp),,然后建立应用程序对象(theApp),就可以产生一个自己需要的窗口(即需要什么样就在InitInstance()里创建就行了)。

整个程序,就改写一个InitInstance()函数,创建那么一个对象(theApp),就是一个完整的窗口程序。

这就是“黑箱”作业的魅力!

在我们正想为微软鼓掌的时候,我们突然觉得心里空荡荡的,我们想知道微软帮我们做了什么事情,而我们想编自己的程序时又需要做什么事情,那怕在上面几行的程序里面,我们还有不清楚的地方,比如,干嘛有一个m_pMainWnd指针变量,它从哪里来,又要到哪里去呢?

想一想在DOS下编程是多么美妙的一件事呵,我们需要什么变量,就声明什么变量,需要什么样的函数,就编写什么样的函数,或者引用函数库……但是现在我们怎么办!

我们可以逆向思维一下,MFC要达到这种效果,它是怎么做的呢?

首先我们要弄明白,VC不是一种语言,它就象我们学c语言的时候的一个类似记事本的编辑器(请原谅我的不贴切的比喻),所以,在VC里面我们用的是C++语言编程,C++才是根本(初学者总是以为VC是一门什么新的什么语言,一门比C++先进很多的复杂语言,汗)。

说了那么多,我想用一句简单的话概括“MFC‘黑箱’就是帮助我们插入了‘C++代码’的东西”。

既然MFC黑箱帮我们插入了代码,那么大家想想它会帮我们插入什么样的代码呢?

他会帮我们插入求解一元二次方程的代码吗?

当然不会,所以它插入的实际上是每次编写窗口程序必须的,通用的代码。

再往下想,什么才是通用的呢?

我们每次视窗编程都要写WinMain()函数,都要有注册窗口,产生窗口,消息循环,回调函数……即然每次都要的东西,就让它们从我们眼前消失,让MFC帮忙写入!

要知道MFC初始化过程,大家当然可以跟踪执行程序。

孙老师的第三课跟踪了很长一段时间,我相信大家都有点晕头转向。

本人觉得那怕你理解了MFC代码,也很容易让人找不着北,我们完全不懂的时候,在成千上万行程序的迷宫中如何能找到出口?

我们要换一种方法,不如就来重新编写个MFC库吧,哗!

大家不要笑,小心你的大牙,我不是疯子(虽然疯子也说自己不疯)。

我们要写的就是最简单的MFC类库,就是把MFC宏观上的,理论上的东西写出来。

我们要用最简化的代码,简化到刚好能运行。

既然,我们这一节写的是MFC程序的初始化过程,上面我们还有了一个可执行的MFC程序。

程序中只是用了两个MFC类,一个是CWinApp,另一个是CFrameWnd。

当然,还有很多同样重要MFC类如视图类,文档类等等。

但在上面的程序可以不用到,所以暂时省去了它(总之是为了简单)。

好,现在开始写MFC类库吧……唉,面前又有一个大难题,就是让大家背一下MFC层次结构图。

天,那张鱼网怎么记得住,但既然我们要理解他,总得知道它是从那里派生出来的吧。

考虑到大家都很辛苦,那我们看一下上面两个类的父子关系(箭头代表派生):

CObject->

CCmdTarget->

CWinThread->

CWinApp->

自己的重写了InitInstance()的应用程序类。

CObject(同上)->

CCmdTarget(同上)->

CWnd->

CFrameWnd

看到层次关系图之后,终于可以开始写MFC类库了。

按照上面层次结构,我们可以写以下六个类(为了直观,省去了构造函数和析构函数)。

classCObiect{};

//MFC类的基类。

classCCmdTarget:

publicCObject{};

------------------------------------------------

classCWinThread:

publicCCmdTarget{};

classCWinApp:

publicCWinThread{};

classCWnd:

classCFrameWnd:

publicCWnd{};

大家再想一下,在上面的类里面,应该有什么?

大家马上会想到,CWinApp类或者它的基类CCmdTarget里面应该有一个虚函数virtualBOOLInitInstance(),是的,因为那里是程序的入口点,初始化程序的地方,那自然少不了的。

可能有些朋友会说,反正InitInstance()在派生类中一定要重载,我不在CCmdTarget或CWinApp类里定义,留待CWinApp的派生类去增加这个函数可不可以。

扯到这个问题可能有点越说越远,但我想信C++的朋友对虚函数应该是没有太多的问题的。

总的来说,作为程序员如果清楚知道基类的某个函数要被派生类用到,那定义为虚函数要方便很多。

也有很多朋友问,C++为什么不自动把基类的所有函数定义为虚函数呢,这样可以省了很多麻烦,这样所有函数都遵照派生类有定义的函数就调用派生类的,没定义的就调用基类的,不用写virtual的麻烦多好!

其实,很多面向对象的语言都这样做了。

但定义一个虚函数要生成一个虚函数表,要占用系统空间,虚函数越多,表就越大,有时得不偿失!

这里哆嗦几句,是因为往后要说明的消息映射中大家更加会体验到这一点,好了,就此打往。

上面我们自己解决了一个问题,就是在CCmdTarge写一个virtualBOOLInitInstance()。

大家再下想,我们还要我们MFC“隐藏”更多的东西:

WinMain()函数,设计窗口类,窗口注册,消息循环,回调函数……我们马上想到封装想封装他们。

大家似乎隐约地感觉到封装WinMain()不容易,觉得WinMain()是一个特殊的函数,许多时候它代表了一个程序的起始和终结。

所以在以前写程序的时候,我们写程序习惯从WinMain()的左大括写起,到右大括弧返回、结束程序。

我们换一个角度去想,有什么东西可以拿到WinMain()外面去做,许多初学者们,总觉得WinMain()函数天大的函数,什么函数都好象要在它里面才能真正运行。

其实这样了解很片面,甚至错误。

我们可以写一个这样的C++程序:

iostream.h>

classtest{

test(){cout<

<

"

请改变你对main()函数的看法!

endl;

}

testtest1;

/**************************/

voidmain(){}

在上面的程序里,入口的main()函数表面上什么也不做,但程序执行了(注:

实际入口函数做了一些我们可以不了解的事情),并输出了一句话(注:

全局对象比main()首先运行)。

现在大家可以知道我们的WinMain()函数可以什么都不做,程序依然可以运行,但没有这个入口函数程序会报错。

那么WinMain()函数会放哪个类上面呢,请看下面程序:

AfxMessageBox("

程序依然可以运行!

大家可以看到,我并没有构造框架,而程序却可以运行了——弹出一个对话框(如果没有WinMain()函数程序会报错)。

上面我这样写还是为了直观起见,其实我们只要写两行程序:

CWinApptheApp;

//整个程序只构造一个CWinApp类对象,任可事情,程序就可以运行!

所以说,只要我们构造了CWinApp对象,就可以执行WinMain()函数。

我们马上相信WinMain()函数是在CWinApp类或它的基类中,而不是在其他类中。

其实这种看法是错误的,我们知道编写C++程序的时候,不可能让你在一个类中包含入口函数,WinMain()是由系统调用,跟我们的平时程序自身调用的函数有着本质的区别。

我们可以暂时简单想象成,当CWinApp对象构造完的时候,WinMain()跟着执行。

现在大家明白了,大部分的“通用代码(我们想封装隐藏的东西)”都可以放到CWinApp类中,那么它又是怎样运行起来的呢?

为什么构造了CWinApp类对象就“自动”执行那么多东西。

大家再仔细想一下,CWinApp类对象构造之后,它会“自动”执行自己的构造函数。

那么我们可以把想要“自动”执行的代码放到CWinApp类的构造函数中。

那么CWinApp类可能打算这样设计(先不计较正确与否):

publicCWinThead{

virtualBOOLInitInstance();

//解释过的程序的入点

CWinApp:

:

CWinApp(){//构造函数

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

WinMain();

//这个是大家一眼看出的错误

Create();

//设计、创建、更新显示窗口

Run();

//消息循环

写完后,大家又马上感觉到似乎不对,WinMain()函数在这里好象真的一点用处都没有,并且能这样被调用吗(请允许我把手按在圣经上声明一下:

WinMain()不是普通的函数,它要肩负着初始化应用程序,包括全局变量的初始化,是由系统而不是程序本身调用的,WinMain()返回之后,程序就结束了,进程撤消)。

再看Create()函数,它能确定设计什么样的窗口,创建什么样的窗口吗?

如果能在CWinApp的构造函数里确定的话,我们以后设计MFC程序时窗口就一个样,变得写程序变有必要。

再看Run()函数,它能在WinMain()函数外面运行吗?

回过头来,我们可以让WinMain()函数一条语句都不包含吗?

不可以,我们看一下WinMain()函数的四个参数:

WinMain(HINSTANCE,HINSTANCE,LPSTR,int)

其中第一个参数指向一个实例句柄,我们在设计WNDCLASS的时候一定要指定实例句柄。

我们窗口编程,肯定要设计窗口类。

所以,WinMain()再简单也要这样写:

intWinMain(HINSTANCEhinst,HINSTANCEhPrevInstance,LPSTRlpCmdLine,intnCmdShow)

{hInstance=hinst}

既然实例句柄要等到程序开始执行才能知道,那么我们用于创建窗口的Create()函数也要在WinMain()内部才能执行[因为如果等到WinMain()执行完毕后,程序结束,进程撤消,当然Create()也不可能创建窗口]

那么Run()(消息循环)放在那里执行好呢?

众所周知,消息循环就是相同的那么几句代码,但我们也不要企图把它放在WinMain()函数之外执行。

所以我们在WinMain()函数里面,我们程序要象以下这样写

WinMain(……)

……窗口类对象执行创建窗口函数……

……程序类对象执行消息循环函数……

对于WinMain()的问题,得总结一下,我们封装的时候是不可以把它封装到CWinApp类里面,但由于WinMain()的不变性(或者说有规律可循),MFC完全有能力在我们构造CWinApp类对象的时候,帮我们完成那几行代码。

转了一个大圈,我们仿佛又回到了SDK编程的开始。

但现在我们现在能清楚地知道,表面上MFC与SDK编程截然不同,但实质上MFC只是用类的形式封装了SDK函数,封装之后,我们在WinMain()函数中只需要几行代码,就可以完成一个窗口程序。

我们也由此知道了应如何去封装应用程序类(CWinApp)和主框架窗口类(CFrameWnd)。

下面把上开始设计这两个类。

为了简单起见,我们忽略这两个类的基类和派生类的编写,可能大家会认为这是一种很不负责任的做法,但本人觉得这既可减轻负担,又免了大家在各类之间穿来穿去,更好理解一些(我们在关键的地方作注明)。

还有,我把全部代码写在同一个文件中,让大家看起来不用那么吃力,但这是最不提倡的写代码方法,大家不要学哦!

windows.h>

HINSTANCEhInstance;

classCFrameWnd

HWNDhwnd;

CFrameWnd();

//也可以在这里调用Create()

virtual~CFrameWnd();

intCreate();

//类就留意这一个函数就行了!

BOOLShowWnd();

classCWinApp1

CFrameWnd*m_pMainWnd;

//在真正的MFC里面

//它是CWnd指针,但这里由于不写CWnd类

//只要把它写成CFrameWnd指针

CWinApp1*m_pCurrentWinApp;

//指向应用程序对象本身

CWinApp1();

virtual~CWinApp1();

virtualBOOLInitInstance();

//MFC原本是必须重载的函数,最重要的函数!

virtualBOOLRun();

//消息循环

CFrameWnd:

CFrameWnd(){}

~CFrameWnd(){}

intCFrameWnd:

Create()//封装创建窗口代码

WNDCLASSwndcls;

wndcls.style=0;

wndcls.cbClsExtra=0;

wndcls.cbWndExtra=0;

wndcls.hbrBackground=(HBRUSH)GetStockObject(WHITE_BRUSH);

wndcls.hCursor=LoadCursor(NULL,IDC_CROSS);

wndcls.hIcon=LoadIcon(NULL,IDC_ARROW);

wndcls.hInstance=hInstance;

wndcls.lpfnWndProc=DefWindowProc;

//默认窗口过程函数。

//大家可以想象成MFC通用的窗口过程。

wndcls.lpszClassName="

窗口类名"

;

wndcls.lpszMenuName=NULL;

RegisterClass(&

wndcls);

hwnd=CreateWindow("

"

窗口实例标题名"

WS_OVERLAPPEDWINDOW,0,0,600,400,NULL,NULL,hInstance,NULL);

return0;

BOOLCFrameWnd:

ShowWnd()//显示更新窗口

ShowWindow(hwnd,SW_SHOWNORMAL);

UpdateWindow(hwnd);

CWinApp1:

CWinApp1()

m_pCurrentWinApp=this;

~CWinApp1(){}

//以下为InitInstance()函数,MFC中要为CWinApp的派生类改写,

//这里为了方便理解,把它放在CWinApp类里面完成!

//你只要记住真正的MFC在派生类改写此函数就行了。

BOOLCWinApp1:

InitInstance()

m_pMainWnd=newCFrameWnd;

m_pMainWnd->

Create();

ShowWnd();

Run()//封装消息循环

MSGmsg;

while(GetMessage(&

msg,NULL,0,0))

TranslateMessage(&

msg);

DispatchMessage(&

}//封装消息循环

CWinApp1theApp;

//应用程序对象(全局)

intWINAPIWinMain(HINSTANCEhinst,HINSTANCEhPrevInstance,LPSTRlpCmdLine,intnCmdShow)

hInstance=hinst;

CWinApp1*pApp=theApp.m_pCurrentWinApp;

//真正的MFC要写一个全局函数AfxGetApp,以获取CWinApp指针。

pApp->

InitInstance();

Run();

代码那么长,实际上只是写了三个函数,一是CFrameWnd类的Create(),第二个是CWinApp类的InitInstance()和Run()。

在此特别要说明的是InitInstance(),真正的MFC中,那是我们跟据自己构造窗口的需要,自己改写这个函数。

大家可以看到,封装了上面两个类以后,在入口函数WinMain中就写几行代码,就可以产生一个窗口程序。

在MFC中,因为WinMain函数就是固定的那么几行代码,所以MFC绝对可以帮我们自动完成(MFC的特长就是帮我们完成有规律的代码),所以我们创造MFC应用程序的时候,看不到WinMain函数。

MFC六大关键技术之剖析(第二部分)

二、运行时类型识别(RTTI)

运行时类型识别(RTTI)即是程序执行过程中知道某个对象属于某个类,我们平时用C++编程接触的RTTI一般是编译器的RTTI,即是在新版本的VC++编译器里面选用“使能RTTI”,然后载入typeinfo.h文件,就可以使用一个叫typeid()的运算子,它的地位与在C++编程中的sizeof()运算子类似的地方(包含一个头文件,然后就有一个熟悉好用的函数)。

typdid()关键的地方是可以接受两个类型的参数:

一个是类名称,一个是对象指针。

所以我们判别一个对象是否属于某个类就可以象下面那样:

if(typeid(ClassName)==typeid(*ObjectName)){

((ClassName*)ObjectName)->

Fun();

象上面所说的那样,一个typeid()运算子就可以轻松地识别一个对象是否属于某一个类,但MFC并不是用typeid()的运算子来进行动态类型识别,而是用一大堆令人费解的宏。

很多学员在这里很疑惑,好象MFC在大部分地方都是故作神秘。

使们大家编程时很迷惘,只知道在这里加入一组宏,又在那儿加入一个映射,而不知道我们为什么要加入这些东东。

其实,早期的MFC并没有typeid()运算子,所以只能沿用一个老办法。

我们甚至可以想象一下,如果MFC早期就有template(模板)的概念,可能更容易解决RTTI问题。

所以,我们要回到“古老”的年代,想象一下,要完成RTTI要做些什么事情。

就好像我们在一个新型(新型到我们还不认识)电器公司里面,我们要识别哪个是电饭锅,哪个是电磁炉等等,我们要查看登记的各电器一系列的信息,我们才可以比较、鉴别,那个东西是什么!

要登记一系列的消息并不是一件简单的事情,大家可能首先想到用数组登记对象。

但如果用数组,我们要定义多大的数组才好呢,大了浪费空间,小了更加不行。

所以我们要用另一种数据结构——链表。

因为链表理论上可大可小,可以无限扩展。

链表是一种常用的数据结构,简单地说,它是在一个对象里面保存了指向下一个同类型对象的指针。

我们大体可以这样设计我们的类:

structCRuntimeClass

……类的名称等一切信息……

CRuntimeClass*m_pNextClass;

//指向链表中下一CRuntimeClass对象的指针

链表还应该有一个表头和一个表尾,这样我们在查链表中各对象元素的信息的时候才知道从哪里查起,到哪儿结束。

我们还要注明本身是由哪能个类派生。

所以我们的链表类要这样设计:

CRuntimeClass*m_pBaseClass;

//指向所属的基类。

//定义表尾的时候只要定义此指针为空就可以了。

staticCRuntimeClass*pFirstClass;

//这里表头指针属于静态变量,因为我们知道static变量在内存中只初始化一次,就可以为各对象所用!

保证了各对象只有一个表头。

有了CRuntimeClass结构后,我们就可以定义链表了:

staticCRuntimeClassclassCObject={NULL,NULL};

//这里定义了一个CRuntimeClass对象,因为classCObject无基类,所以m_pBaseClass为NULL。

因为目前只有一个元素(即目前没有下一元素),所以m_pNextClass为NULL(表尾)。

至于pFirstClass(表头),大家可能有点想不通,它到什么地方去了。

因为我们这里并不想把classCObject作为链表表头,我们还要在前面插入很多的CRuntimeClass对象,并且因为pFirstClass为static指针,即是说它不是属于某个对象,所以我们在用它之前要先初始化:

CRuntimeClass*CRuntimeClass:

pFirstClass=NULL;

现在我们可以在前面插入一个CRuntimeClass对象,插入之前我得重要申明一下:

如果单纯为了运行时类型识别,我们未必用到m_pNextClass指针(更多是在运行时创建时用),我们关心的是类本身和它的基类。

这样,查找一个对象是否属于一个类时,主要关心的是类本身及它的基类,

CRuntimeClassclassCCmdTarget={&

classCObject,NULL};

CRuntimeClassclassCWnd={&

classCCmdTarget,NULL};

CRuntimeClassclassCView={&

展开阅读全文
相关资源
猜你喜欢
相关搜索
资源标签

当前位置:首页 > 解决方案 > 学习计划

copyright@ 2008-2023 冰点文库 网站版权所有

经营许可证编号:鄂ICP备19020893号-2