};
testtest1;
/**************************/
voidmain(){}
////////////////////////////////////////////////////
在上面的程序里,入口的main()函数表面上什么也不做,但程序执行了(注:
实际入口函数做了一些我们可以不了解的事情),并输出了一句话(注:
全局对象比main()首先运行)。
现在大家可以知道我们的WinMain()函数可以什么都不做,程序依然可以运行,但没有这个入口函数程序会报错。
那么WinMain()函数会放哪个类上面呢,请看下面程序:
#include
classMyApp:
publicCWinApp
{
public:
BOOLInitInstance()//②程序入点
{
AfxMessageBox("程序依然可以运行!
");
returntrue;
}
};
MyApptheApp;//①建立应用程序。
大家可以看到,我并没有构造框架,而程序却可以运行了——弹出一个对话框(如果没有WinMain()函数程序会报错)。
上面我这样写还是为了直观起见,其实我们只要写两行程序:
#include
CWinApptheApp;
//整个程序只构造一个CWinApp类对象,任可事情,程序就可以运行!
所以说,只要我们构造了CWinApp对象,就可以执行WinMain()函数。
我们马上相信WinMain()函数是在CWinApp类或它的基类中,而不是在其他类中。
其实这种看法是错误的,我们知道编写C++程序的时候,不可能让你在一个类中包含入口函数,WinMain()是由系统调用,跟我们的平时程序自身调用的函数有着本质的区别。
我们可以暂时简单想象成,当CWinApp对象构造完的时候,WinMain()跟着执行。
现在大家明白了,大部分的“通用代码(我们想封装隐藏的东西)”都可以放到CWinApp类中,那么它又是怎样运行起来的呢?
为什么构造了CWinApp类对象就“自动”执行那么多东西。
大家再仔细想一下,CWinApp类对象构造之后,它会“自动”执行自己的构造函数。
那么我们可以把想要“自动”执行的代码放到CWinApp类的构造函数中。
那么CWinApp类可能打算这样设计(先不计较正确与否):
classCWinApp:
publicCWinThead{
public:
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)。
下面把上开始设计这两个类。
为了简单起见,我们忽略这两个类的基类和派生类的编写,可能大家会认为这是一种很不负责任的做法,但本人觉得这既可减轻负担,又免了大家在各类之间穿来穿去,更好理解一些(我们在关键的地方作注明)。
还有,我把全部代码写在同一个文件中,让大家看起来不用那么吃力,但这是最不提倡的写代码方法,大家不要学哦!
#include
HINSTANCEhInstance;
classCFrameWnd
{
HWNDhwnd;
public:
CFrameWnd(); //也可以在这里调用Create()
virtual~CFrameWnd();
intCreate(); //类就留意这一个函数就行了!
BOOLShowWnd();
};
classCWinApp1
{
public:
CFrameWnd*m_pMainWnd;//在真正的MFC里面
//它是CWnd指针,但这里由于不写CWnd类
//只要把它写成CFrameWnd指针
CWinApp1*m_pCurrentWinApp;//指向应用程序对象本身
CWinApp1();
virtual~CWinApp1();
virtualBOOLInitInstance();//MFC原本是必须重载的函数,最重要的函数!
!
!
!
virtualBOOLRun();//消息循环
};
CFrameWnd:
:
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);
return0;
}
/////////////
CWinApp1:
:
CWinApp1()
{
m_pCurrentWinApp=this;
}
CWinApp1:
:
~CWinApp1(){}
//以下为InitInstance()函数,MFC中要为CWinApp的派生类改写,
//这里为了方便理解,把它放在CWinApp类里面完成!
//你只要记住真正的MFC在派生类改写此函数就行了。
BOOLCWinApp1:
:
InitInstance()
{
m_pMainWnd=newCFrameWnd;
m_pMainWnd->Create();
m_pMainWnd->ShowWnd();
return0;
}
BOOLCWinApp1:
:
Run()//////////////////////封装消息循环
{
MSGmsg;
while(GetMessage(&msg,NULL,0,0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return0;
}//////////////////////////////////////////////////////封装消息循环
CWinApp1theApp; //应用程序对象(全局)
intWINAPIWinMain(HINSTANCEhinst,HINSTANCEhPrevInstance, LPSTRlpCmdLine,intnCmdShow)
{
hInstance=hinst;
CWinApp1*pApp=theApp.m_pCurrentWinApp;
//真正的MFC要写一个全局函数AfxGetApp,以获取CWinApp指针。
pApp->InitInstance();
pApp->Run();
return0;
}
代码那么长,实际上只是写了三个函数,一是CFrameWnd类的Create(),第二个是CWinApp类的InitInstance()和Run()。
在此特别要说明的是InitInstance(),真正的MFC中,那是我们跟据自己构造窗口的需要,自己改写这个函数。
大家可以看到,封装了上面两个类以后,在入口函数WinMain中就写几行代码,就可以产生一个窗口程序。
在MFC中,因为WinMain函数就是固定的那么几行代码,所以MFC绝对可以帮我们自动完成(MFC的特长就是帮我们完成有规律的代码),所以我们创造MFC应用程序的时候,看不到WinMain函数。
写到这里,MFC六大关键技术之一:
MFC程序的初始化过程(模拟),就差不多写完了。
回头看一下,居然写了八千多字,原本以为写完六大关键技术也不用写那么多字,现在还觉得庆幸的是不把文档、视类牵连进去,否则更不知写到何时。
还有五大关键技术没有写,我还应该写下去吗?
上面写了八千多字,都是我一个字一个字地敲进去,每个例子都是自己生硬地想出来。
用了十多个小时,换来的可能更多是论坛中朋友们的漫骂,讥讽!
但我觉得还是值得的,我一向认为VC没有敌人,只有朋友,放眼周围,发觉学VC的朋友越来越少,也没有发现多少招收VC程序员的地方。
记得读大学的时候,我遇到一位搞美术的师兄,本来同行如敌国(我曾经搞过美术)。
师兄美术功底很好,但他从来没有在学校获过美术一等奖,原因评奖的不懂得欣赏他的作品。
我的出现,他深刻地体会到了:
多一个朋友,会少一分孤独!
有时觉得学习VC的朋友是英雄(但我不是英雄,因为我学VC多年来无甚突破),是值得尊敬的人物,大家交流一下,纠正一下自己的错误,真是一种福份……
本人的QQ:
14653353,E_mail:
liyi268@(这是两男两女共用的邮箱,可能不一定是我收到,若注明小李先生收另当别论)欢迎联系。
类别:
c++|
|添加到搜藏|分享到i贴吧|浏览(53)|评论 (0)
追根究底,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在大部分地方都是故作神秘。
使们大家编程时很迷惘,只知道在这里加入一组宏,又在那儿加入一