VC++深入详解笔记.docx
- 文档编号:26196297
- 上传时间:2023-06-17
- 格式:DOCX
- 页数:118
- 大小:159.88KB
VC++深入详解笔记.docx
《VC++深入详解笔记.docx》由会员分享,可在线阅读,更多相关《VC++深入详解笔记.docx(118页珍藏版)》请在冰豆网上搜索。
VC++深入详解笔记
孙鑫《VC++深入详解》
学习笔记
2015年5月
目录
一、C++基础1
二、MFC框架程序剖析4
三、简单绘图5
四、文本编程6
五、菜单7
六、对话框
(一)11
七、对话框
(二)14
八、定制应用程序外观15
九、绘图控制16
十、图形的保存和重绘17
十一、文件和注册表操作18
十二、文档与串行化19
十三、网络编程20
十四、多线程25
十五、线程同步与异步套接字编程29
十六、进程间通信34
十七、ActiveX控件41
十八、动态链接库44
十九、HOOK和数据库访问49
一、C++基础
结构体:
类:
类与结构体的区别:
除关键字不同(结构体为struct,类为class)外,更重要的是在成员的访问控制方面的差异。
结构体默认情况下其成员是公有的(public);类默认情况下其成员是私有的(private)。
在一个类当中,公有成员是可以在类的外部进行访问的,而私有成员就只能在类的内部进行访问了。
类与对象:
类描述了一类事物,以及事物所应具有的属性。
对象是类的具体实例。
构造函数:
(1)C++提供构造函数用来对类中的成员变量进行初始化。
规定构造函数的函数名和类名相同,没有返回值。
(2)构造函数的作用是对对象本身做初始化工作,也就是给用户提供初始化类中成员变量的一种方式。
(3)如果一个类中没有定义任何的构造函数,那么C++编译器在某些情况下会为该类提供一个默认的构造函数,这个默认的构造函数是一个不带参数的构造函数。
只要一个类中定义了一个构造函数,不管这个构造函数是否是带参数的构造函数,C++编译器就不再提供默认的构造函数。
也就是说,如果为一个类定义了一个带参数的构造函数,还想要无参数的构造函数,则必须自己定义。
注意:
若一个类中没定义任何构造函数,编译器只在以下三种情况才会提供默认的构造函数
(1)如果类有虚拟成员函数或者虚拟继承父类(即有虚拟基类)时;
(2)如果类的基类有构造函数(可以是用户定义的构造函数,或编译器提供的默认构造函数);
(3)在类中的所有非静态的对象数据成员,它们所属的类中有构造函数(可以是用户定义的构造函数,或编译器提供的默认构造函数)。
析构函数:
~类名()
析构函数是反向的构造函数。
不允许有返回值,更重要的是析构函数不允许带参数,并且一个类中只能有一个析构函数。
用于清除类的对象。
对于一个对象来说,析构函数是最后一个被调用的成员函数。
函数的重载:
类中函数名相同,但是参数的类型和个数不同的两个函数可以同时作为一个类的两个构造函数,C++编译器会根据参数的类型和参数的个数来确定执行哪一个构造函数。
构成函数重载的条件:
函数的参数类型、参数个数不同,才能构成函数的重载。
(注意:
只有函数的返回类型不同是不能构成函数重载的,并且应注意函数带有默认参数的情况,如voidoupt(inta,intb=5);)
this指针:
this指针式一个隐含的指针,它是指向对象本身的,代表了对象的地址。
类的继承:
当构造子类的对象时,它需要先构造父类的对象,调用父类的构造函数,然后再构造子类的对象。
析构的时候是先析构子类对象,再析构父类对象。
多重继承:
class派生类名:
访问权限基类名,访问权限基类名,访问权限基类名
{
……
}
多态性:
在基类的函数前加上virtual关键字,在派生类中重写该函数,运行时将会根据对象的实际类型来调用相应的函数。
纯虚函数:
指被标明为不具体实现的虚成员按函数(也可以由函数体,但是很少见)。
凡是含有纯虚函数的类叫做抽象类,这种类不能声明对象,指示作为基类为派生类服务。
在派生类中必须完全实现基类的纯虚函数,否则,派生类也变成了抽象类,不能实例化对象。
函数覆盖:
条件:
(1)基类函数必须是虚函数(使用virtual关键字进行声明)
(2)发生覆盖的两个函数要分别位于派生类和基类中
(3)函数名称与函数列表必须完全相同
函数隐藏:
是指派生类中具有与基类同名的函数(不考虑参数列表是否相同)
两种情况:
(1)派生类的函数与基类的函数完全相同(函数名和参数列表都相同),指示基类函数没有使用virtual关键字。
此时基类的函数将被隐藏
(2)派生类的函数与基类的函数同名,单参数列表不同,在这种情况下,不管基类的函数声明是否有virtual关键字,基类的函数都将被隐藏。
注意:
(1)当隐藏发生的时候,如果在派生类的同名函数中想要调用基类的被隐藏函数,可以使用——基类名:
:
函数名(参数)——的语法格式
(2)函数的覆盖发生在派生类与基类之间,两个函数必须完全相同,并且都是虚函数。
除此之外就是隐藏了。
引用:
就是一个变量的别名,需要用另一个变量或者对象来初始化自身。
用&表示申明一个引用,必须在申明时进行初始化。
引用与指针变量的区别:
(1)引用指示一个别名,是一个变量或对象的替换名称。
引用的地址没有任何意义,因此C++没有提供访问引用本身地址的方法。
引用的地址就是它所引用的变量或者对象的地址,对引用的地址所做的操作就是对被引用的变量或对象的地址所做的操作。
(2)指针是地址,指针变量要存储地址值,因此要占用存储空间,我们可以随时修改指针变量所保存的地址值,从而指向其他的内存。
C++类的设计习惯:
通常将类的定义及类成员函数的声明放到头文件(.h文件中);
将类中成员函数的实现放到源文件(.cpp文件中)。
往一个现有工程添加头文件或源文件两种方式:
(1)在打开的工程中,File→New,在左边的Files标签页下选C++HeaderFile或C++SourceFile,然后在右边的File文本框中输入头文件或源文件的文件名,单击OK按钮。
(2)在工程目录下,新建文本文档,修改文件名为“头文件名.h”或“源文件名.cpp”,然后在打开的工程中,选Project→AddtoProject→Files,选择新建的那些文件,单击OK按钮。
:
:
叫做作用域标示符,用于指明一个函数属于哪个类或一个数据成员属于哪个类。
:
:
前面如果不跟类名,表示是全局函数(即非成员函数)或全局数据。
二、MFC框架程序剖析
单文档应用程序:
都有一个CmainFrame类,一个C+工程名+App类,一个C+工程名+Doc类,一个C+工程名+View类。
全局变量都放在ClassView标签页的Globals分支下。
注:
如果某个函数的参数有默认值,那么在调用该函数时可以传递该参数的值,也可以不传递,直接使用默认值即可。
MFC中以Afx-为前缀的函数都是全局函数,可以在程序的任何地方调用它们。
后缀为-Ex的函数都是扩展函数
MFC程序的运行机制:
设计窗口类(MFC中已经预定义了一些窗口类,我们可以直接调用);注册窗口类,创建窗口,显示并更新窗口,消息循环
(1)首先利用全局应用程序对象theAPP启动应用程序。
(2)调用全局应用程序对象的构造函数,从而就会先调用器基类CwinApp的构造函数。
(3)进入WinMain函数。
在AfxWinMain函数中可以获取子类的指针,利用辞职真调用虚函数InitInstance,据多态性原理实际上调用的是子类的InitInstance函数。
(4)进入消息循环。
当时收到WM_QUIT消息时,退出消息循环,程序结束。
窗口类、窗口类对象与窗口
C++窗口类对象与窗口并不是一回事,它们之间为宜的关系是C++窗口类对象内部定义了一个窗口句柄变量,保存了与这个C++窗口类对象相关的那个窗口的句柄。
窗口销毁时,与之对应的C++窗口类对象销毁与否,要看其生命周期是否结束。
但C++窗口类对象销毁时,与之相关的窗口也将销毁。
MFC的消息映射机制:
一个MFC消息响应函数在程序中有三处相关信息:
函数原型,函数实现,以及用来关联消息和消息响应函数的宏
头文件中在两个AFX_MSG注释宏之间是消息响应函数原型的声明。
源文件中有两处:
一是在两个AFX_MSG_MAP注释宏之间的消息映射宏,通过这个宏把消息与消息响应函数关联起来;另一处是源文件中的消息响应函数的实现代码。
只要在MFC中定义与消息相关的这三处信息后,就可以实现消息的响应处理,这就是MFC的消息映射机制。
MFC消息映射机制的具体方法是:
在每个能接受和处理消息的类中,定义一个消息和消息函数静态对照表,即消息映射表。
在消息映射表中,消息与对应的消息处理函数指针是成对出现的。
某个类能处理的所有消息及其对应的消息处理函数的地址都列在这个类所对应的静态表中。
当有消息需要处理时,程序只要搜索该消息静态表,产看表中是否含有该消息,就可知道该类能否处理此消息。
如果能处理该消息,则同样依照静态表能很容易找到并调用对应的消息处理函数。
三、简单绘图
在程序中,当构造一个GDI对象之后,该对象并不会立即生效,必须选入设备描述表,它才会在以后的绘制操作中生效。
利用SelectObject函数可以实现吧GDI对象选入设备描述表,并且该函数返回指向先前被选对象的指针。
主要是为了在完成当前绘制操作之后,还原设备描述表用的。
一般情况下,在完成绘图操作之后,都要利用SelectObject函数吧先前的GDI对象选入设备描述表,以便使其恢复到先前的状态。
CPen类用来创建画笔对象
Cbrush类用来创建画刷对象
设备描述表中是有一个默认的白色画刷的
静态成员函数:
静态成员函数和静态成员变量属于类本身,在类加载的时候,即为它们分配了空间。
也就是说,在还没有产生类的任何一个对象时,静态成员函数和静态成员变量就已经存在于程序的代码区了。
所以可以通过“类名:
:
函数名”或“类名:
:
变量名”这种方式来访问。
而非静态成员函数和非静态成员变量属于对象的方法和数据,也就是应该首先产生类的对象,然后通过类的对象去调用。
因此,在静态成员函数中是不能调用非静态成员的,包括非静态成员函数和非静态成员变量。
但是由于静态成员(包括函数和变量)属于类本身,在类的对象产生之前就已经存在了,所以在非静态成员函数中是可以调用静态成员的。
实际上,关于函数之间的引用许可,可以从内存模型这一角度来考虑。
也就是说,无论采用什么样的操作,程序代码都是在内存中运行的,只有在内存中占有了一席之地,我们才能够访问它。
如果一个成员函数或成员变量还未在内存中产生,结果是无法访问它的。
根据上面的内容,可以推导出,静态成员函数只能访问静态成员变量。
并且静态成员变量必须在类的定义之外对其进行初始化。
需要注意的是,如果没有初始化静态成员变量,同时在main程序中也没有访问这些静态变量,那么程序建立时也不会报错。
四、文本编程
创建文本插入符:
voidCreateSolidCaret(intnWidth,intnHeight);
新创建的文本插入符初始是隐藏的,需要用ShowCaret()将其显示出来
创建图形插入符:
voidCreateCaret(Cbitmap*pBitmap);
调用CDC类的GetTextMetrics()函数可以得到设备描述表中当前字体的度量信息
VC++开发环境中定义字符串资源:
ResourceView选项卡中有StringTable(字符串表),双击打开字符串标,要添加新的字符串资源,可以再最底部的空行上双击,即可弹出添加新字符串的资源对话框,进行操作即可。
路径层(pathbracket):
创建路径层是通过调用CDC类提供的BeginPath函数来实现的,作用是在设备描述表中打开一个路径层;然后利用图形设备接口(GDI)提供的绘图函数进行绘图操作;最后在绘图操作完成之后,应用程序通过调用EndPath函数关闭这个路径层。
CDC类提供的GetTextExtent函数可以获得一个字符串在屏幕上显示的高度和宽度:
CsizeGetTextExtent(constCstring&str)const;此函数需要一个Cstring对象的引用作为其参数。
注:
GetTextMetrics函数,获得的是设备描述表中当前字体的度量信息,而GetTextExtent函数则是获得某个特定的字符串在窗口中显示时所占据的宽度和高度。
裁剪区域(clippingregion):
可以把它理解为一个绘图区域。
CDC类提供了SelectClipPath函数,作用是把当前设置的路径层和设备描述表中已有的裁剪区域按照一种指定的模式进行一个互操作:
BOOLSelectClipPath(intnMode);
字符输入:
需要捕获键盘按下这一消息。
按下退格键时将字体设置成背景色,再显示原字符减1;按下回车键时,光标移到下一行是用GetTextMetrics函数获得当前设备描述表中字体的高度,并加在当前插入点的纵坐标处就行。
设置字体:
Cfont类,它的初始化函数有CreateFont,CreateFontIndirect,CreatePointFont,CreateFontIndirect。
字幕变色功能:
CDC类的DrawText函数来实现,其作用是在指定的矩形范围内输出文字。
可以通过定时器来自动控制文字变色的进程。
注:
对一个变量进行自加或自减操作之前,一定要初始化这个变量,否则结果是不确定的。
五、菜单
MFC中,把设置为Pop-up类型的菜单成为弹出式菜单,VisualC++默认顶层菜单为弹出式菜单,这种菜单不能响应命令。
为了区分资源类型,在ID后加上相应的字母:
如菜单资源以IDM_开始,光标资源以IDC_开始,图标资源以IDI_开始等等。
程序对菜单命令的响应顺序:
为单文档程序的应用程序类(C**App),文档类(C**Doc)和视类(C**View)以及主框架类(CMainFrame)分别添加菜单的响应函数(App和Doc类不是从CWnd类派生的,可以用AfxMessageBox函数或者使用全局的MessageBox函数),运行程序结果是视类最先响应菜单命令。
然后将视类的响应函数删除,运行程序结果是文档类响应了菜单命令;接着将文档类的响应函数删除,运行程序结果是主框架类相应了菜单命令;再将主框架类的响应函数删除,运行程序结果是应用程序类响应了菜单命令。
根据上述经验,可以得知,响应菜单项命令的顺序依次是:
视类、文档类、主框架类、应用程序类。
Windows消息分类:
分为三类,分别是标准消息、命令消息和通稿消息。
标准消息:
除WM_COMMAND消息外,所有以WM_开头的消息都是标准消息。
从CWnd派生的类,都可以接收到这类消息。
命令消息:
来自菜单、加速键或工具栏按钮的消息。
这类消息都以WM_COMMAND形式呈现在MFC中,通过菜单项的标识(ID)来区分不同的命令消息;在SDK中,通过消息的wParam参数识别。
从CcmdTarget派生的类,都可以接收到这类消息。
通告消息:
由控件产生的消息,如按钮的单击、列表框的选择等都会产生这类消息,目的是为了向其父窗口(通常是对话框)通知事件的发生。
这类消息也是以WM_COMMAND形式呈现的。
从CCmdTarget派生的类,都可以接收到这类消息。
通过MSDN提供的MFC类层次结构图,可以发现CWnd类实际上派生于CcmdTarget类。
也就是说,凡是从CWnd派生的类,它们既可以接收标准消息,也可以接收命令消息和通告消息。
而对于那些从CCmdTarget派生的类,则只能接收命令消息和通告消息,不能接收标准消息。
菜单命令的路由:
为视类添加响应函数,发现在三个地方添加了与菜单命令消息响应函数相关的信息。
首先是视类头文件中,两个AFX_MSG注释宏之间添加了命令消息响应函数原型;然后是视类源文件中有两处:
一处是在AFX_MSG_MAP注释宏之间添加了ON_COMMAND宏,将菜单ID号与命令响应函数关联起来;另一处是在视类源文件中的命令消息响应函数的实现。
具体过程:
当点击某个菜单项的时候,最先接收到这个菜单命令消息的是框架类。
框架类将把接收到的这个消息交给它的子窗口,即视类,由视类首先进行处理。
视类首先根据命令消息映射机制查找自身是否对此消息进行了响应,如果响应了,就调用相应的响应函数对这个消息进行处理,消息路由过程结束;如果视类没有对此命令消息做出响应,就交由文档类,文档类同样查找自身是否对这个菜单命令进行了响应,如果响应了,就交由文档类的命令消息响应函数进行处理,路由过程结束。
如果文档类也未做出响应,就把这个命令消息交还给视类,后者又把该消息交还给框架类。
框架类查看自己是否对这个命令消息进行了响应,如果它也没有做出响应,就把这个菜单消息交给应用程序类,由后者来进行处理。
标记菜单:
菜单项前带有√的菜单,用CheckMenuItem(UINTuItem,UINTnCheck)函数实现,第一个参数有第二个参数决定,若nCheck为MF_BYPOSITION,则第一个参数为菜单项的索引号(如0,1,…);若nCheck为MF_BYCOMMAND,则第一个参数为菜单项的命令ID(如ID_FILE_OPEN等)。
默认菜单项:
菜单项是以粗体形式显示的菜单项。
并且一个子菜单中只能有一个默认的菜单项。
用SetDefaultItem函数实现。
注:
分隔栏在子菜单中是占据索引位置的。
图形标记菜单:
用SetMenuItemBitmaps(UINTnPosition,UINTnFlags,constCbitmap*pBmpUnchecked,constCbitmap*pBmpChecked)函数实现。
使用下面代码可以知道图形标记菜单项上要显示的位图的尺寸大小:
CStringstr;
str.Format("x=%d,y=%d",GetSystemMetrics(SM_CXMENUCHECK),GetSystemMetrics(SM_CYMENUCHECK));
MessageBox(str);
禁用菜单项:
用EnableMenuItem(UINTnIDEnableItem,UINTnEnable)函数实现,通常把MF_GRAYED和MF_DISABLED这两个标志放在一起使用,实现菜单项禁用时变灰。
(注意:
需要在CMainFrame类的构造函数中将变量m_bAutoMenuEnable初始化成FALSE,EnableMenuItem函数就能够正常工作了)。
移除和装载菜单:
使用BOOLSetMenu(Cmenu*pMenu)函数(参数为NULL)来实现移除菜单的功能;使用LoadMenu函数来加载菜单并使用SetMenu(Cmenu*pMenu)函数(参数为要加载菜单的对象的指针)来将其显示出来。
(注意:
如果定义的是局部菜单对象,则一定要在调用SetMenu函数设置窗口菜单后,立即调用菜单对象的Detach函数将菜单句柄与菜单对象分离)
MFC菜单命令更新机制:
UPDATE_COMMAND_UI消息的响应只能应用于菜单项,不能应用于永久显示的顶级菜单(即弹出式菜单)项目。
添加了次消息响应函数,MFC在后台所做的工作是:
当要显示菜单的时候,操作系统发出WM_INITMENUPOPUP消息,然后由程序窗口的积累如CFrameWnd接管。
它会创建一个CCmdUI对象,并与程序的第一个菜单项相关联,调用该对象的一个成员函数DoUpdate(),这个函数才发出CN_UPDATE_COMMAND_UI消息,这条消息带有一个指向CCmdUI对象的指针。
这时,系统会判断是否存在一个ON_UPDATE_COMMAND_UI宏去捕获这个菜单项消息。
如果找到这样一个宏,就调用相应的消息响应函数进行处理,在这个函数中,可以利用传递过来的CCmdUI对象去调用响应的函数,使该菜单项可以使用,或禁用该菜单项。
当更新完第一个菜单项后,同一个CCmdUI对象就设置为与第二个菜单项相关联,依此顺序进行,直到完成所有菜单项的处理。
这就是MFC采用的命令更新机制。
另外,CCmdUI类还有一个m_nID成员变量,用于保存当前菜单项、工具栏按钮,或者是其他由CCmdUI对象表示的标识。
还有一个m_nIndex成员变量,保存了当前菜单项的位置索引。
工具栏按钮与菜单项的关联:
如果要把工具栏上的一个工具按钮与菜单栏中的某个菜单项相关联,只要将他们的ID设置为同一个标识就可以了。
如果要在程序中设置某个菜单项的状态,首先通过ClassWizard为这个菜单项添加UPDATE_COMMAND_UI消息响应函数,然后在这个函数中进行状态的设置即可(注:
菜单项和工具按钮的位置索引计算方式不同,并且工具栏上的分隔符也要计算在索引内)。
快捷菜单:
使用函数BOOLTrackPopupMenu(UINTnFlags,intx,inty,CWnd*pWnd,LPCRECTlpRect=NULL)来显示快捷菜单。
对于快捷菜单,如果将其拥有者窗口设置为框架类窗口,则框架类窗口才能有机会获得对该快捷菜单中的菜单项的命令响应,否则,就只能由视类窗口做出响应。
动态菜单操作:
一种是针对弹出菜单的动态操作,一种是针对菜单项的动态操作。
(1)添加菜单项目:
使用CMenu类的成员函数AppendMenu可以把一个新菜单项目添加到一个指定菜单项目的末尾。
声明形式:
BOOLAppendMenu(UINTnFlags,UINTnIDNewItem=0,LPCTSTRlpszNewItem=NULL)
使用CMenu类成员函数CreatePopupMenu可以创建一个弹出菜单。
(2)插入菜单项目:
使用CMenu类的成员函数InsertMenu可以实现在已有菜单项目之间插入一个新的菜单项目(包括在两个子菜单之间插入一个子菜单,以及在两个菜单项之间插入一个新的菜单项)。
声明形式:
BOOLInsertMenu(UINTnPosition,UINTnFlags,UINTnIDNewItem=0,LPCTSTRlpszNewItem=NULL)
若要在新插入的菜单中添加菜单项的话可以使用AppendMenu函数。
若要在已有菜单中添加菜单项,可以再CmainFrame类的OnCreate函数中进行添加。
(3)删除菜单:
使用CMenu类的成员函数DeleteMenu可以实现删除一个菜单项目,包括子菜单,以及子菜单下的菜单项,主要取决于调用这个函数的对象,如果该对象是程序的菜单栏对象,那么删除的就是指定的子菜单;如果该对象是一个子菜单对象,那么删除的就是该子菜单下的一个菜单项。
(4)动态添加的菜单项的命令响应:
首先是为该菜单项创建一个菜单资源ID(FileView选项卡上的HeaderFiles下打开Resource.h,在其中添加#defineIDM_HELLO111);接着添加命令响应函数(一是在响应该菜单项命令的程序类头文件中添加响应函数原型,在两个AFX_MSG注释宏之后;二是在响应该菜单项命令的程序类源文件中的消息映射表中添加消息映射,在两个AFX_MSG_MAP注释宏之后;三是菜单命令响应函数的定义体)
注:
(1)在C/C++语言中,字符串的索引是从0开始的。
(2)OnCommand函数是对所有的命令消息进行路由处理的,包括菜单、工具栏按钮,以及加速键的命令消息。
(3)LOWORD宏从给定的32位值中取得低端字,HIWORD宏从给定的32位值中取得高端字。
(4)可以使用CmainFrame类提供的GetActiveView成员函数,获取与框架相关联的当前视类的指针。
CView*GetAct
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- VC 深入 详解 笔记