【MFC学习日志】基于对话框的MFC程序框架理解

新建一个基于对话框的MFC应用程序,创建后会产生4个文件和3个类:
(假例创建MFC程序名为Test)

4个文件:

  • Test.h 应用程序类头文件
  • Test.cpp 应用程序类源文件(实现文件)
  • TestDlg.h 对话框类头文件
  • TestDlg.cpp 对话框类源文件

3个类:

  • CTestApp 应用程序类,由Test.h 和 Test.cpp 声明和定义。
  • CTestDlg 对话框类,由TestDlg.h 和 TestDlg.cpp 声明和定义。
  • CAboutDlg 关联对话框类,声明和定义均在TestDlg.cpp 对话框类源文件中。

Test.h :

// Test.h : PROJECT_NAME 应用程序的主头文件
#pragma once //保证头文件只被编译一次

#ifndef __AFXWIN_H__ //非活动预处理,包含Afxwin.h文件
	#error "在包含此文件之前包含“stdafx.h”以生成 PCH 文件"
#endif

#include "resource.h"		// 主符号 //包含相应头文件

// CTestApp:
// 有关此类的实现,请参阅 Test.cpp
//
class CTestApp : public CWinApp	//CtestApp类声明
{
public:
	CTestApp(); //构造函数
// 重写
public:
	virtual BOOL InitInstance();	//重写InitInstance 初始化实例函数
// 实现
	DECLARE_MESSAGE_MAP()	//声明消息映射表
};
extern CTestApp theApp;	//此处声明了一个应用程序对象。

#pragma once

在使用预编译指令#include 时,为避免重复声明造成二义性,在文件开头写入#pragma once 指令保证相关头文件只被包含编译一次。
同样也可以使用 #ifndef 指令

#ifndef _CODE_BLOCK
#define _CODE_BLOCK
// code
#endif// _CODE_BLOCK 

#ifndef方式是C/C++语言的标准支持,也是比较常用的方式,#ifndef的方式依赖于自定义的宏名(例中的_CODE_BLOCK)不能冲突,它不光可以保证同一份文件不会被包含两次,也能够保证不同文件完全相同的内容不会被包含两次。但,同样的,如果自定义的宏名不小心“重名”了,两份不同的文件使用同一个宏名进行#ifndef,那么会导致编译器找不到声明的情况(被编译器判定为重定义而屏蔽了)。
#ifndef可以针对一个文件中的部分代码,而#pragma once只能针对整个文件。相对而言,#ifndef更加灵活,兼容性好,#pragma once操作简单,效率高。
参考: https://blog.csdn.net/cv_jason/article/details/81842710

#ifndef#define#endif
预编译阶段把所有#include ”***.h" 用 ***.h的内容来替换了, 所以之后就没有.h了所有.h的内容都已经包含进了需要它们的.cpp中。
当一个简单的project中有三个文件main.cpp, a.cpp, a.h,而 main.cpp 和a.cpp分别包含了a.h, 在编译阶段, 两个编译单元是都会分别包含a.h的, 即使他们使用了#ifndef#define#endif, 这也是为什么当a.h被多个文件包含时我们不允许在a.h中定义变量及函数的原因, 因为在链接阶段会出现重定义。 但是在a.h中定义一个static变量却是允许的, 因为static变量是模块性作用域, 就这个例子来说, 若我们在a.h中写static int sss = 0;那么main.cpp与a.cpp使用的sss将为2个独立的sss。
当a.cpp中写了多个#include "a.h"时, 如果使用了#ifndef#define#endif那么预编译阶段就只会包含一个a.h中的内容到a.cpp中, 当main.cpp中包含了a.h和b.h, 而a.h中我们又包含了b.h, 那么如果使用了#ifndef#define#endif则main.obj只会包含一份b.h。

#pragma 预编译指令简介

#pragma 作用是设定编译器的状态或者是指示编译器完成一些特定的动作; 其所定义的很多指示字是编译器特有的,在不同的编译器间是不可移植的

#pragma parameter

message 参数在编译时输出消息到编译输出窗口中 ,可用于条件编译中可提示代码的版本信息。与 #error 和 #warning 不同,#pragma message 仅仅代表一条编译消息,不代表程序错误。

示例,在TestDlg.cpp中添加#pragma message指令:

#include "stdafx.h"
#include "Test.h"
#include "TestDlg.h"
#include "afxdialogex.h"

#ifdef _DEBUG
#define new DEBUG_NEW
#endif

#pragma message ("Test message!");

程序编译后输出:

1>------ 已启动生成: 项目: Test, 配置: Debug Win32 ------
1>  TestDlg.cpp
1>  Test message!
1>c:\users\z8519\desktop\程序\test\test\testdlg.cpp(14): warning C4081: 应输入“newline”;找到“;1>  Test.vcxproj -> C:\Users\z8519\Desktop\程序\Test\Debug\Test.exe
========== 生成: 成功 1 个,失败 0 个,最新 0 个,跳过 0==========
#pragma pack

#pragm pack 用于指定内存对齐方式
C语言中,不同类型的数据在内存中按照一定的规则排列,而不一定是顺序的一个接一个的排列。
为什么需要内存对齐?
CPU 对内存的读取不是连续的,而是分成块读取的,块的大小只能是1、2、4、8、16…字节。
当读取操作的数据未对齐,则需要两次总线来访问内存,因此性能会大打折扣。
某些硬件平台只能从规定的相对地址读取特定类型的数据,否则产生硬件异常
未对齐造成两次内存读取【32位机器的读写最小粒度4字节】

在这里插入图片描述
其他:
#program hdrstop
表示预编译头文件到此为止,后面的头文件不进行编译。
#program resource
#program resource “.dfm”表示把.dfm文件中的资源添加到工程。
#program comment
将一个注释记录放入一个对象文件或可执行文件。
#program data_seg
用来建立一个新的数据段并定义共享数据。

参考:https://blog.csdn.net/czg13548930186/article/details/86980147
http://www.cnblogs.com/runningRain/p/5936788.html

Test.cpp:

// Test.cpp : 定义应用程序的类行为。
#include "stdafx.h" //包含头文件
#include "Test.h"
#include "TestDlg.h"

#ifdef _DEBUG //debug模式下,定位内存位置
#define new DEBUG_NEW
#endif
/*
DEBUG_NEW的定义: #define DEBUG_NEW new(THIS_FILE, __LINE__)
当在debug模式下时,我们分配内存时的new被替换成DEBUG_NEW,
而这个DEBUG_NEW不仅要传入内存块的大小,还要传入源文件名和行号,
这就有个好处,即当发生内存泄漏时,我们可以在调试模式下定位到该问题代码处。若删掉该句,就不能进行定位了。
而在release版本下的new就是简单的new,并不会传入文件名和行号。
因此,我们在开发代码阶段,保留上述代码是值得的。
*/
// CTestApp 

BEGIN_MESSAGE_MAP(CTestApp, CWinApp) //消息映射表
	ON_COMMAND(ID_HELP, &CWinApp::OnHelp)
END_MESSAGE_MAP()


// CTestApp 构造
CTestApp::CTestApp()
{
	...;
}
// 唯一的一个 CTestApp 对象
CTestApp theApp;
// CTestApp 初始化
BOOL CTestApp::InitInstance()
{
	CWinApp::InitInstance();
	CTestDlg dlg;
	m_pMainWnd = &dlg;
	INT_PTR nResponse = dlg.DoModal(); //打开对话框
	if (nResponse == IDOK)
	{
		// TODO: 在此放置处理何时用
		//  “确定”来关闭对话框的代码
	}
	else if (nResponse == IDCANCEL)
	{
		// TODO: 在此放置处理何时用
		//  “取消”来关闭对话框的代码
	}
	else if (nResponse == -1)
	{
		TRACE(traceAppMsg, 0, "警告: 对话框创建失败,应用程序将意外终止。\n");
		TRACE(traceAppMsg, 0, "警告: 如果您在对话框上使用 MFC 控件,则无法 #define _AFX_NO_MFC_CONTROLS_IN_DIALOGS。\n");
	}
	return FALSE;
}

TestDlg.h :

// TestDlg.h : 头文件
#pragma once

// CTestDlg 对话框
class CTestDlg : public CDialogEx 
{
// 构造
public:
	CTestDlg(CWnd* pParent = NULL);	// 标准构造函数
// 对话框数据
	enum { IDD = IDD_TEST_DIALOG };	//关联度画框ID
	protected:
	virtual void DoDataExchange(CDataExchange* pDX);	// DDX/DDV 支持
// 实现
protected:
	HICON m_hIcon;	

	// 生成的消息映射函数
	virtual BOOL OnInitDialog();
	afx_msg void OnSysCommand(UINT nID, LPARAM lParam);
	afx_msg void OnPaint();	//重绘时调用。
	afx_msg HCURSOR OnQueryDragIcon();	//回调函数,可有可无。
	DECLARE_MESSAGE_MAP()	
};

afx_msg
afxwin.h 中定义:

// Type modifier for message handlers
#ifndef afx_msg
#define afx_msg         // intentional placeholder
#endif

可见,afx_msg 并无实际意思,它是一个消息处理函数的前缀。类向导生成的消息函数,分发函数,事件响应函数都以这个为前缀。如果去掉了,向导将不能识别。

借鉴他人知识,粗陋理解,不断完善自身。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值