[MFC]匈牙利标记法、MFC基本结构

1. 常用匈牙利标记法小结:

变量名前缀: 数据类型

b: BOOL

c/ch: 字符(可以是char也可以是wchar_t)

clr: COLOREF

cx, cy: 水平和垂直距离

dw: DWORD

h: handle

l: 长整型

n: 整型

p: pointer

sz: 以零结尾的字符串(string end with zero)

w: word

str: CString

wnd: CWnd

m_: 类成员


!以上部分是Windows API中定义的数据类型而并非C语言的基本数据类型;

!以C打头的类型都属于MFC中定义的类型,C就是Class的意思;


2. MFC对Windows API的封装:

    1) 不仅对普通的窗口操作等进行了面向对象的封装(比如将窗口句柄作为窗口类的成员变量来处理等),同时将程序上升到进程的层面,MFC对进程的相关属性也进行了面向对象的封装;

    2) 以CWinApp类为例,MFC将应用程序自身封装在该类中,使程序员可以对应用程序的运行环境等特性进行控制(或者说是比API编程更简单直观了);

    3) WinMain在MFC程序中将不可见(隐藏在MFC源码中),程序是通过WinMain调用CWinApp的成员函数Run来运行引用程序本身的,因此在MFC中可以将WinMain看做操作系统的功能,应用程序这个类的总体功能由程序员设计好后将自动交由操作系统的WinMain来运行(目前就这样理解好了);


3. 文档/视图结构:

    1) MFC的一大特色将数据和视图分离开来,视图结构专门负责数据的图形化显示,而文档部分则专注于对程序数据的存储和处理;

    2) MFC基本结构中的4大类:CDocument(文档类)、CView(视图类)、CWinApp(应用程序本身的抽象)、CFrameWnd(窗口类,Windows要求每个应用程序都必须要有一个(至少)主窗口,该类就是该主窗口的抽象,该类封装了Win32 API中的WNDCLASS类,并且可以将其实例化自动注册给操作系统);

!有些应用程序不是没有窗口吗?是的,只不过那些程序将窗口设为不显示,但并不代表它没有窗口,内存中有其窗口的所有资源;

    3) 在这4个类中CWinApp和CFrameWnd两个类是必不可少的,前两个类可有可无,而我们的第一个MFC程序就只有后面两个类,只不过没有前两个类的MFC程序没有什么使用功能罢了;


4. MFC类的层次结构:

    1) 大多数MFC类都是直接或间接地从CObject类继承而来(类似Java的Object类),但是也有部分类不是由CObject类继承而来,比如简单类型CPoint类等(其实就是只包含没几个成员变量的结构体),还有一些和网络协议相关的类,比如Http类等,当然用户也可以自己定义类并选择不继承CObject类;

    2) CObject类提供的3种重要功能:

         i. 串行化:负责对象中的数据流入流出存储介质(如磁盘等);

         ii. 运行时类型信息支持:RTCI,比RTTI更加古老,是多态的一种表现,即支持在程序运行的时候检查数据对象的类型名称以及其他信息,是RTTI和多态的基础;

         iii. 支持诊断和调试:存在调试和诊断的相关代码,当然可以在发布产品的时候加一些编译选项去掉这些部分;


5. MFC的全局函数:

    1) MFC并非严格的面向对象,而是用面向对象的方法包装的了结构化的Win32 API,因此它具有结构化程序的执行效率,因此MFC是以效率优先的,一些高性能的游戏都是用MFC编写,因为直接用Win32 API编写过于复杂,而MFC具有面向对象特性,不仅可以简化编程复杂度也不失执行效率;

    2) MFC为方便程序编写提供了很多全局函数,即Afx打头,即应用程序框架函数(Application Framework Function,最后的x没有意义,只是看上去比较帅而已),这些函数可以在全局范围内使用,而不受类作用范围的限制,很多基本上都完全等价于Win32 API,如AfxMessageBox和Win32的MessageBox完全等价,只不过加了Afx前缀可以在逻辑上区分MFC和Win32 API,可以防止命名空间的冲突,这使得在MFC程序中调用Win32 API更加方便了;


6. MFC程序的基本组成:

    1) 本章中要求掌握的类层次结构有:

CObeject

|_CCmdTarget

    |_CWinThread

    |   |_CWinApp

    |_CWnd

        |_CFrameWnd

!其中CCmdTarget是消息映射的基础,Windows操作系统中的所有对象都离不开消息机制,包括窗口、进程、线程等,因此像线程、窗口等类型都是从该类继承而来;

!而应用程序本事就是一种线程,因此CWinApp理所当然地由CWinThread继承而来;

    2) 第一个MFC程序——Hello MFC!

!C/C++习惯将声明和定义分离开来,因此该程序分为Hello.h和Hello.cpp文件;

!!真正标准的MFC源程序做法是为代码中定义的每个类都单独做一个相应的.h和.cpp文件,就目前阶段而言,程序相对简单,就将所有类都放在一起写,因此只有一个.h和一个.cpp文件;

// Hello.h

// 声明MFC程序最重要切必不可少的两个类

// 应用程序本身需要从CWinApp继承
// 可以给该类添加一些属于自己的新特性
class CMyApp: public CWinApp
{
// 其中继承来了一个重要的成员变量,即应用程序的主窗口对象的指针
// 即m_pMainWindow,将一个窗口对象赋给该变量的过程相当于Win32
// RegisterClass的过程

public:
	// 其中必须覆盖基类继承的虚函数InitInstance
	// 因为窗口的创建和初始化都位于该函数中
	// 该函数默认只有一条return TRUE语句
	// 所以必须覆盖该函数才行
	virtual BOOL InitInstance();

	// virtual int ExitInstance();
	// 也可以覆盖该函数,一般只有在InitInstance中使用new
	// 分配内存时才会在该函数中使用delete回收内存
	// 这里没用用到该功能,因此此函数并没有覆盖
};

// 应用程序的主窗口需要从CFrameWnd继承
// 此类中包含Win32窗口类类型(WNDCLASS)
// 并且含有窗口资源的句柄作为其成员变量
class CMyWnd: public CFrameWnd
{
public:
	// 构造函数,需要在里面使用Create创建窗口资源
	// 可以指定需要的窗口风格
	CMyWnd(); 

protected:
	// 定义消息映射
	// 功能是将接收到的消息映射到消息处理函数去
	// 微软在内部使用了一系列复杂的宏来实现消息映射
	   // 目的是为了避免使用虚函数(虚函数的虚表占用大量内存)
	   // 虽然内部原理复杂,但不过使用起来仅仅就是一下示范的简单格式

	afx_msg void OnPaint(); // 首先要声明消息处理函数,可以声明多个
	// afx_msg ...
	// afx_msg ...
	// ...
	DECLARE_MESSAGE_MAP() // 声明消息映射宏一定要放在最后一句,其实也是整个窗口类的最后一句
};

// Hello.cpp

#include <afxwin.h> // 包含MFC的核心类,诸如CWinApp等,在Hello.h中使用过

#include "Hello.h"

// 说先创建一个表示MFC应用程序自身的对象
// 注意!应用程序自身的对象有且只能有一个!
CMyApp myApp;

// ------------------------CMyApp类的定义-----------------------------

BOOL CMyApp::InitInstance()
{
	m_pMainWnd = new CMyWnd; // 创建一个自定义的窗口类型对象
	                         // 该赋值过程相当于RegisterClass

	m_pMainWnd->ShowWindow(m_nCmdShow); // 指定窗口显示的风格(最大化还是最小化)
	                                    // 这里使用默认的显示风格,使用成员变量m_nCmdShow指定
	                                    // 操作系统会给WinMain传递一个默认的该参数的值
	                                    // 注意!该参数有WinMain传入

	m_pMainWnd->UpdateWindow(); // 刷新客户区,默认使用OnPaint刷新

	return TRUE;
}

// 在Win32中讲过,ShowWindow用于显示窗口的框架(即非客户区的部分,比如标题,菜单等等)
// 而UpdateWindow用于刷新并显示客户区的部分,除框架以外的部分
// 可以通过在ShowWindow调用出加断点调试看出


// ------------------------CMyWnd类的定义---------------------------

// 一般最先定义消息映射
// 消息映射在MFC中也属于类的成员,因此也需要指定所属的类,继承自CCmdTarget
// 可以看到在BEGIN_MESSAGE_MAP中有两个参数,第一个就是所属类
// 而第二个规定必须是该类的基类
// 最后的END_MESSAGE_MAP宏无参数
// 两者之间是所有的窗口类中使用到的消息映射的消息条目的列表
BEGIN_MESSAGE_MAP(CMyWnd, CFrameWnd)
	ON_WM_PAINT() // 消息条目
END_MESSAGE_MAP()

// 成员函数定义
CMyWnd::CMyWnd()
{
	Create(NULL, _T("The Hello Application"));
}

void CMyWnd::OnPaint()
{
	CPaintDC dc(this);
	CRect rect;

	GetClientRect(&rect);
	dc.DrawText(_T("Hello MFC!"), -1, &rect,
		DT_SINGLELINE | DT_CENTER | DT_VCENTER);
}

    3) 关于消息ID、消息条目(即消息宏)、消息响应函数之间的关系:

         i. 消息ID就是指以WM_开头的表示消息的整数宏,其中WM是指Window Message的意思;

         ii. 消息条目就是指BEGIN_MESSAGE_MAP和END_MESSAGE_MAP之间的条目,也称为消息宏,在Afxmsg_.h中定义;

         iii. 消息响应函数就是指在窗口类的protected域中定义的"OnXXX"的成员函数了;

         iv. 三者之间的关系即为MFC消息映射的原理,其实在DECLARE_MESSAGE_MAP和BEGIN_MESSAGE_MAP中维护了一张消息映射表,当扫描到消息条目的时候就会将相应的消息ID映射到类中的消息响应函数中去,比划说在这里就相当于message_map_table[WM_PAINT] = &CMyWnd::OnPaint;

         v. 三者之间的命名关系:命名都是严格规定好的,从WM_PAINT、ON_WM_PAINT、OnPaint三者之间就能非常容易地推断出咱这名字之间的关联了;

     4) 关于afx_msg宏定义:既然放在函数返回类型之前就意味着这应该是一种函数调用约定,这个宏表示空白,即什么也没有,也就是默认的stdcall了,在这里仅仅就是表意而已,表示这是一个消息响应函数,并且是一种行为很想虚函数但并非虚函数的函数;

!!小技巧:将一个消息映射到MFC没定义的消息相应函数

像OnPaint、OnLButtonDown之类的消息响应函数都是MFC事先定义的,但是也存在某些情况,希望用一个自己创造的函数来相应相应的消息,比如使用OnMySetText来响应WM_SETTEXT而不是使用默认的OnSetText来相应WM_SETTEXT;

!技巧就是利用一般化的消息条目ON_MESSAGE(可以表示任何类型的消息,同时也意味着可以映射任何类型的消息),在声明消息条目的时候显示地进行映射,比如在上面的例子中就是ON_MESSAGE(WM_SETTEXT, OnMySetText),这样就实现了将消息映射到自定义的任意函数上去了;

由于是采用ON_MESSAGE的一般化映射,因此OnMySetText的接口应该也应该采用一般化的消息相应接口,即:afx_msg LRESULT OnMySetText(WPARAM wParam, LPARAM lParam);

     5) 消息条目的实质就是定义消息映射:

          i. 一般性的消息映射定义就是ON_MESSAGENAME(MessageID, Handler),即第一个参数是消息ID,第二个参数就是映射目标,即将MessageID代表的消息交由Handler来处理;

          ii. 但是可以看到一些消息映射没有任何参数,这是因为这些消息映射都在MFC的源码中定义好了,比如WM_PAINT到OnPaint的映射,除了消息ID在任何程序中都不能改变外,其余包括消息条目名ON_WM_PAINT、响应函数名OnPaint等都已经在MFC的源码中定义好了,所以可以省去ON_MESSAGE(WM_PAINT, OnPaint)这样的写法,而直接使用MFC中默认的省略式写法ON_WM_PAINT();


!!!消息映射宏中包含public、protected等访问控制符,因此最好将DECLARE_MESSAGE_MAP写在类定义最后,如果硬是想在DECLARE_MESSAGE_MAP后面添加其它内容,一定要添加新的访问控制符!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值