深入浅出MFC-学习笔记 Day 1

深入浅出MFC-学习笔记

  • VC基础巩固学习-MFC
  • 第一章 Win32基本程序概念
  • 第二章 C++ 的重要性质
    • 类及其成员: 谈封装(encapulation)
    • 基类与派生类: 谈继承(Inheritance)
    • 虚函数与多态(Polymorphism)
      • 虚函数与一般化
      • 多态(Polymorphism)
    • 静态成员 (变量和函数)
    • C++程序的生与死: 兼谈构造函数与析构函数
      • 四种不同的对象生存方式(in stack、 in heap、 global、 local static)

VC基础巩固学习-MFC


一些名词

  • API:Application Programming Interface
  • SDk: SoftWare Development Kit
  • MFC: Microsoft Foundation Classes
  • OWL: Object Windows Library

第一章 Win32基本程序概念


Windows支持动态链接。 Windows程序调用的函数可分为C Runtimes以及Windows API两大部分。

LIBC.LIB 这是C Runtime函数库的静态链接版本。
MSVCRT.LIB 这是C Runtime函数库到动态链接版本。(程序执行时必须有MSVCRT40.DLL在场)


Windows程序设计最重要的观念:模块将各类消息分类到所属的队列中去,然后以这些消息为基础的事件驱动系统。


CreateWindow()只产生窗口,并不显示窗口。利用ShowWindow()将它显示在屏幕上。

  • 窗口只需注册一次,“只有第一个实例才会进入”的InitApplication函数中,所以RegisterClass操作写在这个函数里面。
  • 产生窗口,每个实例都需要进入InitInstance函数中。所以把CreateWindow()写在这个函数里面。

  • 程序的进入,以及窗口的注册和产生
  • 程序通过WinMain()进入,通过InitApplication()函数注册窗口,在InitApplication()里封装类窗口类,使用RegisterClas()来初始化注册窗口信息,通过InitInstance()函数为实例创建窗口,使用CreateWindow()来为实例创建窗口,并且初始化窗口信息。使用ShowWindow()来将窗口显示出来。之后进入消息循环状态,通过GetMessage()非强制性多任务的函数,用DispatchMessage()将消息分派给所属的窗口函数去处理。TranslateMessage()用来转换键盘消息。
int CALLBACK winMain(HINSTANCE hInstance, hInstance hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
{
	MSG msg;

	UNREFERENCED_PARAMETER(lpCmdLine);

	if(!hPrevInstance)      
		if(!InitApplication(hInstance))   //窗口注册
			return (FALSE);

	if(!InitInstance(hInstance, nCmdShow)) //产生窗口
		return (FALSE);

	while(GetMessage(&msg, NULL, 0, 0))  //消息循环
	{
		TranslateMessage(&msg);  //转换键盘消息
		DIspatchMessage(&msg);   //分派消息
	}

	return (msg.wParam);
}
  • 窗口函数通过switch/case指令来分派各类消息。注意的是:无论什么消息都必须被处理,所以default处,调用DefWindowProc,这是Windows内部默认的消息处理函数。
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
     switch (message){
		case WM_COMMAND;
		    ...
		break;

		case WM_DESTORY:
		    ...

		default:
		     return (DefwindowProc(hWnd, message, wParam, lParam));
	}
	return (0);
}

定义成CALLBACK回调函数,是为了当某个消息或时间发生,开放接口给操作系统调用。


  • 封装了消息处理,将“数据”和“处理数理的方法封装”,消息映射的雏形
struct MSGMAP_ENTRY 
{
	UINT nMessage;
	LONG (*pfn)(HWND, UINT, WPARAM, LPARAM);
};
#define dim(x) (sizeof(x) / sizeof(x[0]));
//消息与处理例程的对照表格
struct MSGMAP_ENTRY _messageEntries[] =
{
	WM_CREATE     , OnCreate ,
	WM_PAINT      , OnPaint  ,
	WM_COMMAND    , OnCommand,
	.....
	WM_DESTORY    , OnDestory, 
};
//Command-ID 与处理例程之对照表格
struct MSGMAP_ENTRY _commandEntries[] =
{
	IDM_ABOUT     , OnAbout ,
	IDM_FILEOPEN  , OnFileOpen,
	IDM_SAVEAS    , OnSaveAs  ,
};

//窗口函数
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
	int i;

	for(i = 0; i < dim(_messageEntries); i++)
	{
		if(message == _messageEntries[i].nMessage)
			return ((*_messageEntries[i].pfn)(hWnd, message, wParam, lParam));
	}

	return (DefwindowProc(hWnd, message, wParam, lParam));
}
//专门处理WM_COMMAND
LONG Command(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
	int i;

	for(i = 0; i < dim(_commandEntries); i++)
	{
		if(LOWORD(wParam) == _commandEntries[i].nMessage)
			return ((*_commandEntries[i].pfn)(hWnd, message, wParam, lParam));
	}

	return (DefwindowProc(hWnd, message, wParam, lParam));
}

  • 对话框的运行
  • Windows的对话框依其与父窗口的关系,分为两类:
  1. “令其父窗口无效,直到对话框结束”。 modal对话框
  2. “父窗口与对话框共同运行”。 modaless对话框
  • 做出一个modal对话框,需要两件东西
  1. 对话框模板,这是在RC文件中定义的,决定对话框的大小、字形、内部空、各在什么位置等等。
  2. 对话框函数,形态类似于窗口函数,但是通常只处理WM_INITDIALOG和WM_COMMAND两个消息。每个控件也是一个小窗口,它们有各自的窗口函数。而所有控件传来的消息与其管理者(父窗口,也就是对话框)沟通。
  3. modal对话框的激活与结束。考的是DialogBox和EndDialog两个API函数。
  4. 对话框处理过的消息,应该传回TRUE;未处理消息,则应该传回FALSE。因为对话框没有处理,那么系统提供的默认对话框就会接手处理。
  • 模块定义文件 (.DEF)
  1. Windows程序需要一个模块定义文件,将模块名称、程序段和数据段的内存特性、模块堆大小、堆栈带下、所有callbak函数名称等等记下来。
  2. 在VS集成开发环境中开发程序,不在需要特别准备.DEF文件。
  • 资源描述文件 (.RC)
  1. RC文件是一个以文字描述资源的地方。
  2. 这些文字描述需经过RC编译器,才产生可使用的二进制代码。

Windows程序的生命周期

  1. 程序进入WinMain()中,调取InitInstance()函数中的CreateWindow()创建窗口,产生窗口后会送出WM_CREATE消息给窗口函数,后者可以在此时做些初始化操作,之后通过ShowWindow()显示窗口。
  2. 程序活着的时候,通过GetMessage()抓取消息。如果消息是WM_QUIT,GetMessage()会传回0,结束while循环,进而结束整个程序。
  3. DispatchMessage通过Windows USER模块的协助与监督,把消息分派至窗口函数。消息被判别并处理。
  4. 程序不断进行2和3
  5. 按下系统菜单中的Close命令项时,系统送出WM_CLOSE。通常窗口函数不再拦截此消息,于是DefWindowProc处理它。
  6. DefWindowProc收到WM_CLOSE后,调用DestroyWindow把窗口清除,DestroyWindow本身又会送出WM_DESTORY消息。
  7. 程序对WM_DESTORY的标准反应是调用PostQuitMessage.
  8. PostQuitMessage只会送出WM_CLOSE消息,让GetMessage接收到,执行2结束。

  • 空闲时间的处理:OnOdle )
  1. 所谓的空闲时间就是“系统中没有任何消息等待处理”的时间。
  2. GetMessage: 会过门不入,于是操作系统再去照顾其他人。
  3. PeekMessage: 会取回控制权,使程序得以执行一段时间。

传统的SDK程序如果要处理空闲时间,用一下循环代替WinMain中的传统消息循环

while (TRUE) 
{
	if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
	{
		if(msg.message == WM_QUIT)
			break;
		TranslateMessage(&msg);
		DIspatchMessage(&msg);
	}
	else
	{
		OnIdle();
	}
}
  • Console程序
  1. 调用部分的、不牵扯到图形使用者接口(GUI)的Win32 API。这种程序成为console程序。
  2. 纯“文字窗口”
  • Console程序和DOS程序的差别
  • 编写方式
  1. 利用Windows编译器、链接器做出来的程序。调用C Runtime函数和“不牵扯GUI”的Win32 API函数,进入点是main。是console程序
  2. 过去在DOS环境下开发的程序,成为DOS程序,可以调用C Runtime函数,不能调用Win32 API函数。
  • 程序功能
  1. ConSole功能包含Dos
  • 可运行文件格式
  1. DOS程序是所谓的MZ格式
  2. Console程序的格式和所有的Win32程序一样,是所谓的PE格式。

第二章 C++ 的重要性质

类及其成员: 谈封装(encapulation)

对象的两大属性:属性property,方法method。

  1. 把数据声明为private,不允许外部随意存取,只能通过特定的接口来操作,这就是面向对象的封装特性。

基类与派生类: 谈继承(Inheritance)

抽取共有性质,成立基类。再从中衍生出派生类。派生类继承基类的成员。

  • this指针

  • 成员函数是有一个隐藏参数,名为this指正。

class CShape
{
private:
	int m_color;
public:
	void setcolor(int color) { m_color = color };
}
//编译后
class CShape
{
private:
	int m_color;
public:
	void setcolor(int color, (CShape*)this ) { this->m_color = color };
}

虚函数与多态(Polymorphism)

  • 如果以一个“基类之指针”指向“派生类之对象”,那么经由该指针,只能调用基类所定义的函数。
  • 如果以一个“派生类之指针”指向一个“基类之对象”,必须先做明显的转型操作。
  • 如果基类和派生类都定义了“相同名称的成员函数”,调用哪个函数,由指针的原始类型而定。

虚函数与一般化

如果预期派生类有可能重新定义某一个成员函数,那么你就在基类中把此函数设为virtual。

多态(Polymorphism)

  1. 以相同的指令却调用了不同的函数,这种性质成为Polymorphism。
  2. 虚函数是了解多态以及动态绑定的关键。
  3. 基类的函数什么都不做,就可以将它定义为纯虚函数。拥有纯虚函数的类是抽象类,不能被实例化。
  4. 纯虚函数不需要定义其实际操作,它的存在只是为了在派生类中被重新定义。
  5. 如果派生类没有重新定义一个成员函数,那也不能实例化,于是它也是一个抽象类。
  6. 虚函数派生下去仍为虚函数,而且可以省略virtual关键词。

静态成员 (变量和函数)

当类成员中一个成员变量的值是固定,那么可以在变量前加上static,定义为static成员变量。不要把static成员变量的初始化操作安排的类的构造函数中,因为变量的初值只应该被设定一次。static成员函数同理。

class SavingAcoount
{
private:
	char m_name[40];
	char m_addr[60];
	double m_total;
	static doble m_rate;
	...
public:
	static void setRate(double newRate) { m_rate = newRtae; }
};
doble SavingAcoount::m_rate = 0.0075;
SavingAcoount::setRate(0.0074);

C++程序的生与死: 兼谈构造函数与析构函数

  1. C++的new运算子符和C的malloc函数都是用于配置内存。new的优点是:new不但配置对象所需的内存空间,同时会引发构造函数的执行。
  2. static对象的析构函数比全局对象的析构函数要早一步执行。
  3. 以new方式产生出来的局部对象,析构函数则在对象被delete时执行,否则则会在力离开该对象的活动范围是,其析构函数被执行。

四种不同的对象生存方式(in stack、 in heap、 global、 local static)

你可能感兴趣的:(MFC,2019/5/10,学习笔记)