初学者分析MFC代码4

作者:liguisen

blog:http://blog.csdn.net/liguisen/

在MFC瘦身代码中,你或者已经发现有两个特殊的文件,StdAfx.hS和tdAfx.cpp,它们仅仅是include头文件,什么也没做。如果你够细心,你还会发现在每个cpp文件最上面都include了StdAfx.h,这到底是干什么的呢?可以去掉吗?我们在Test1.cpp把#include "stdafx.h"这一句去掉,编译发现出现这样的错误:fatal error C1010: unexpected end of file while looking for precompiled header directive。意思大概是找不到预编译指示。这个文件到底是干什么用的?其实wizard已经告诉我们答案了,答案就在“注释”   里面!什么?注释?不是已经删掉了吗?是的,就是在我们删掉的注释里面,所以,wizard所做的每一件事都是有道理的。那我们为什么要删掉注释?不就是为了能让你集中注意力看关键的东西嘛!还记得我们一开始备份的工程吗?打开看看:
// stdafx.h : include file for standard system include files,
//  or project specific include files that are used frequently, but
//      are changed infrequently
哦,原来它是用来包含一些标准的系统头文件以及在工程中一些经常用到的但又很少改动的头文件(到此看到我说的话,你应该体会到英语的重要性了)。再看StdAfx.cpp的注释:
// stdafx.cpp : source file that includes just the standard includes
// Test1.pch will be the pre-compiled header
// stdafx.obj will contain the pre-compiled type information
看第一行,的确如此,在stdafx.cpp只有一行#include "StdAfx.h"。Test1.pch是预编译头(可以在工程目录下的debug文件夹找到),到底有什么作用呢?答案是这样的:VC++程序一般包含的头文件都比较复杂,如果每次编译都分析每一行代码(其中很多代码是一直不变的),将耗费大量时间在重复分析上面,因此,有必要把一些不变的结果暂时保存起来,留待下次使用,以节省编译时间。怎么使用Test1.pch呢?我们要指定一个头文件StdAfx.h,当然,也可以是别的名字,在Project->settings里的c/c++页,Category选择Precompiled Headers,选中Use precompiled header file,在Through header里就可以修改名字了。我猜是因为.h文件不能编译,因此还需要一个cpp(stdafx.cpp)文件来生成pch文件。

    既然这个pch预编译头是用来节省时间的,是不是说我们也可以不用?当然,我们有的是时间。首先在Project->settings里的c/c++页,Category选择Precompiled Headers,选中Not using precompiled headers,然后在Test1.cpp和Test1Dlg.cpp中把#include "stdafx.h"去掉,加上StdAfx.h中的内容,跟着在工程里面删掉StdAfx.h和StdAfx.cpp,编译运行都没有问题!当然,这不是一个聪明的做法!不要胡闹了,把关于预编译的修改复原。

    还有一个小问题:为什么StdAfx.h要在cpp中include而不在.h中?那是为了避免头文件的重复包含。

    下面我们来看看这个程序到底是怎么执行的。对于初学者来说,我有一个比较好的方法,姑且称之为“断点大法”。我们把所有代码都加上断点,然后F5进行调试。当然,这样做你会发现vc会提示你有些断点是无效的。好了,GO,一直到程序运行,出来窗口,然后关闭。我把程序执行步骤列出来:

Test1.cpp:
#include .....
CTest1App theApp;/*第1步*/
BOOL CTest1App::InitInstance()
{/*第2步*/
 CTest1Dlg dlg;/*第3步*/
 dlg.DoModal();/*第6步*/ 
 return FALSE;/*第7步*/
}/*第8步*/

Test1Dlg.cpp:
#include .....
CTest1Dlg::CTest1Dlg(CWnd* pParent )
: CDialog(CTest1Dlg::IDD, pParent)
{/*第4步*/
}/*第5步*/
其中,第6步到第7步之间需要你在对话框中点“确定”或“取消”按钮或者点标题栏中的“×”退出程序。

第1步:
    先看看CTest1App theApp的注释:// The one and only CTest1App object。又看注释?呵呵,你可以打开另一个vc,然后打开一开始的工程查看。theApp是应用程序类的一个对象,代表了一个应用程序,这是一个单独的全局的对象。提示:你也可以改为其它的名字。

第2步:
    进入应用程序类的一个InitInstance()函数(注释:// CTest1App initialization,从函数名也能看出来)。

第3步:
    跟着声明一个CTest1Dlg对象dlg。

第4、5步:
    CTest1Dlg类声明一个对象后,立即调用其构造函数(关于构造函数,你需要看相关c++书籍),而这又引起其父类CDialog构造函数的调用。这里要提醒你注意,我们原来的代码是这样的:
在.h中的声明 CTest1Dlg(CWnd* pParent = NULL);
在.cpp中的实现
CTest1Dlg::CTest1Dlg(CWnd* pParent /*=NULL*/)
 : CDialog(CTest1Dlg::IDD, pParent)
注意到了吗?pParent为NULL。(详细信息请查看MSDN中CDialog::CDialog)
    我们着重关注CTest1Dlg::IDD这个参数。IDD从何而来?在头文件中:enum { IDD = IDD_TEST1_DIALOG },你尽可以把名字改掉,例如改成MYIDD:
enum { MYIDD = IDD_TEST1_DIALOG };
CTest1Dlg::CTest1Dlg(CWnd* pParent /*=NULL*/)
 : CDialog(CTest1Dlg::MYIDD, pParent)
还可以这样:
CTest1Dlg::CTest1Dlg(CWnd* pParent /*=NULL*/)
 : CDialog(MYIDD, pParent)
甚至,你可以打开Resource.h找到IDD_TEST1_DIALOG的定义:
#define IDD_TEST1_DIALOG    102
那么你可以这样:
CTest1Dlg::CTest1Dlg(CWnd* pParent /*=NULL*/)
 : CDialog(102, pParent)

这说明了什么?说明这个参数不过是对话框模板资源的ID而已,那么我们可以更改这个ID让程序装载另外一个对话框模板资源。在ResourceView 的Dialog文件夹上右键->insert dialog插入一个对话框,它的默认ID是IDD_DIALOG1,我们这样做:
enum { IDD = IDD_DIALOG1 };
CTest1Dlg::CTest1Dlg(CWnd* pParent /*=NULL*/)
 : CDialog(CTest1Dlg::IDD, pParent)
运行看看。于是我们得到一个启发:在InitInstance()函数里面可以根据不同的条件显示不同的面板

第6步:
    CTest1Dlg对象dlg调用其成员函数DoModal()显示对话框。

第7、8步:
    退出程序。

    MFC程序的流程如此简单(至少目前我们表面上看到的是这样),你还有什么好怕的呢?学习MFC也很容易啊,呵呵,事实并非如此,下次我会结合F10、F11让你看到更多的东西。

 
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值