失眠网,内容丰富有趣,生活中的好帮手!
失眠网 > 孙鑫VC++深入详解第三章学习笔记

孙鑫VC++深入详解第三章学习笔记

时间:2019-06-11 05:05:34

相关推荐

孙鑫VC++深入详解第三章学习笔记

第三章

3.1创建MFC AppWizard

如何利用vs创建MFC应用见参考文献[1]

需要注意的地方有

[1] 创建MFC单文档应用程序

[2]开启类视图窗口

3.2基于MFC的程序框架剖析

在MFC中,类的命名都以C开头;对于单文档应用程序,都有:

CAboutDlg帮助类,同于说明这个工程的开发信息;CMainFrame主框架类;C工程名App应用程序入口;C工程名Doc文档类,用来管理、存放数据;C工程名View用来将文档中的数据可视化。

CMainFrame类和CTestView类都有一个共同的基类:Cwnd类,其封装了与窗口相关的操作。

3.2.1 MFC中的WinMain函数

文件路径(在安装路径下直接搜索MFC,找到mfc):D:\Program Files (x86)\visualstudio\VC\Tools\MSVC\14.29.30037\atlmfc\src,打开appmodul.cpp 查找WinMain

extern "C" int WINAPI_tWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,//右键_tWinMain,单击转到定义:#define _tWinMain WinMain_In_ LPTSTR lpCmdLine, int nCmdShow)#pragma warning(suppress: 4985){// call shared/exported WinMainreturn AfxWinMain(hInstance, hPrevInstance, lpCmdLine, nCmdShow);}

3.2.1.1 theApp全局对象

以简单的C++源程序为例,在入口函数main()加载之前,就已经为全局变量(对象)分配了内存空间,并为其赋了初值。对于一个全局对象来说,此时就会调用该对象的构造函数构造该对象并进行初始化操作,然后才是进入main()函数。(P72例3-4已标出先后顺序)

对于MFC来说,通过产生一个应用程序类的对象来唯一标识应用程序的实例。每个MFC程序有且仅有一个从应用程序类(CWinApp)派生的类;每个MFC程序实例有且仅有一个该派生类的实例化对象,即theApp全局对象,theApp表示了该应用程序本身。theApp的定义如程序3.1所示,在test.cpp中查看。

程序3.1 theApp全局对象

/*test.cpp*/// 唯一的 CtestApp 对象CtestApp theApp; //theApp是CtestApp的一个对象,注意其是一个全局对象/*test.h*/class CtestApp : public CWinApp //CtestApp继承于CWinApp,后者表示应用程序类{......}

文件路径(在安装路径下直接搜索MFC,找到mfc):D:\Program Files (x86)\visualstudio\VC\Tools\MSVC\14.29.30037\atlmfc\src,打开appcore.cpp 查找CWinApp(184行)

其中,

程序3.2

CWinApp::CWinApp(LPCTSTR lpszAppName) //注意此处有参数{... pModuleState->m_pCurrentWinApp = this; //此处this代表子类CTestApp的对象,即theApp...}

程序3.3

class CWinApp : public CWinThread{...explicit CWinApp(LPCTSTR lpszAppName = NULL);// app name defaults to EXE name;构造函数形参有默认值默认值...};

补充:如果某个函数的参数有默认值,那么在调用该函数时可以传参,也可以不传参直接使用默认值。

由程序3.3可见,来CWinApp类的定义时,CWinApp的构造函数的形参有默认值NULL。因此,在调用CWinApp类的构造函数时,不用显式地传参。

3.2.1.2 AfxWinMain函数

WinMain函数实际上是通过调用AfxWinMain函数来完成它的功能的。

其中,Afx前缀的函数代表应用程序框架函数,辅助我们生成应用程序框架的应用模型。在MFC中,Afx为前缀的函数都是全局函数,可以在程序的任何位置调用。

在AfxWinMain函数的定义中,有

程序3.4

/*pThread和pApp这两个指针是一致的,这两个指针都指向CTestApp类的对象,即theApp全局对象*/CWinThread* pThread = AfxGetThread();CWinApp* pApp = AfxGetApp();/*MFC内部管理所调用的函数*/if (pApp != NULL && !pApp->InitApplication())goto InitFailure;/*调用的是子类InitInstance():因为在父类CWinApp中的InitInstance()是虚函数*/if (!pThread->InitInstance()){}/*消息循环*/nReturnCode = pThread->Run();

3.2.1.3 InitInstance函数

见程序3.4的第九行。

3.2.2 MFC框架窗口

3.2.2.1 设计和注册窗口

窗口类的注册是由wincore.cpp的AfxEndDeferRegisterClass函数完成的。AfxEndDeferRegisterClass函数预定义了几种缺省的窗口类,首先判断窗口类的类型,再赋予其相应的类名。部分代码如书本p79所示。

接着调用AfxRegisterClass函数注册从窗口类,该函数首先获得窗口类信息,窗口已经注册,返回真。否则注册该窗口类。

注意:AfxRegisterClass实际上就是AfxEndDeferRegisterClass(宏定义);

3.2.2.2 创建窗口

窗口的创建是由CWnd类中的CreateEx函数完成的。定义:afxwin.h,实现:wincore.cpp。(以Ex结尾的函数表示扩展函数。)

CreateEx函数不是虚函数,CFrameWnd类的Create函数内调用的实际上就是CWnd类的CreatEx函数。

CreateEx函数内部调用的PreCreateWindow函数是一个虚函数,在产生窗口之前有机会修改窗口外观。

3.2.2.3 显示和更新窗口

CTestApp中名为m_pMainWnd的成员变量保存了应用程序框架窗口(CMainFrame)对象的指针,在InitInstance函数(初始化工作:注册、显示、更新)内部:

程序3.5

// 唯一的一个窗口已初始化,因此显示它并对其进行更新m_pMainWnd->ShowWindow(SW_SHOW);//显示m_pMainWnd->UpdateWindow();//更新

3.2.3 消息循环

见程序3.4的第12行。

在thrdcore.cpp中/*消息循环*/ nReturnCode = pThread->Run();

书本p85例3-16,该函数主要结构是一个for循环,在收到WM_QUIT时退出。在循环中的PumpMessage()与第一章的SDK编程的消息处理代码一致。

3.2.4 MFC运行过程梳理

3.3 窗口类、窗口对象与窗口

3.3.1 三者之间的关系

:: 前面没有东西,表示所使用的函数是一个全局函数。如果当前定义的成员函数与内部调用的API函数重名,后者必须加 :: ,否则报错。

C++窗口类对象与窗口并不是一回事,他们之间唯一的关系是C++窗口类对象内部定义了一个窗口句柄变量,保存了与这个C++窗口类对象相关的那个窗口的句柄。窗口销毁时,与之对应的C++窗口类对象销毁与否要看其生命周期是否结束。但C++窗口类对象销毁时,与之相关的窗口也将销毁。

换句话说,在窗口销毁后,CWnd的成员变量m_hWnd设为NULL,并没有被销毁(也有可能被销毁:对象生命周期结束(函数运行到右大括号“}”));而在C++窗口类对象析构时,窗口被销毁。

在系统文件afxwin.h中,CWnd已有一个用于保存句柄的成员变量m_hWnd,ShowWindow()和UpdateWindow()不需要再传递这个句柄,因为它已经是成员变量。

程序3.6

/*afxwin.h*/class CWnd : public CCmdTarget{DECLARE_DYNCREATE(CWnd)protected:static const MSG* PASCAL GetCurrentMessage();// Attributespublic:HWND m_hWnd; // must be first data member......};/*lesson3:\\wainmain.cpp*/int WINAPI WinMain( HINSTANCE hInstance,// handle to current instanceHINSTANCE hPrevInstance, // handle to previous instanceLPSTR lpCmdLine,// command lineint nCmdShow // show state){//设计窗口类...//注册窗口类...//创建窗口类CWnd wnd;wnd.CreateEx(...);wnd.ShowWindow(SW_SHOWNORMAL);wnd.UpdateWindow();/*对比第一章:创建窗口HWND hwnd;hwnd = CreateWindowEx();显示及刷新窗口::ShowWindow(hwnd, SW_SHOWNORMAL);::UpdateWindow(hwnd);注意ShowWindow和UpdateWindow的参数,原因是:CWnd类定义过了一个HWND类型的成员变量m_hWnd用于保存这个窗口的句柄,在调用CWnd类中的ShowWindow显示窗口时,就不在需要传递这个句柄了,因为它已经是成员变量了,该函数可以直接使用它。在窗口销毁后,CWnd的成员变量m_hWnd设为NULL,并没有被销毁;而在C++窗口类对象析构时,窗口被销毁。*///消息循环... return 0; }

3.3.2 在窗口中显示按钮

CButton的Create函数声明:

BOOL Create( LPCTSTR lpszCaption, DWORD dwStyle, const RECT& rect, CWnd* pParentWnd, UINT nID );

lpszCaption:按钮文本;dwStyle:按钮风格+窗口风格;rect:定义一个矩形区域;pParentWnd:指定父窗口。MFC不再通过窗口句柄,而是通过一个与窗口相关的C++窗口类对象指针来传递窗口对象。nID:按钮控件标识。可取整数随机值。在框架窗口产生之后,再创建该标识,否则没地方放置。

如果将按钮创建在CMainFrame::OnCreate()函数内,按钮的父窗口是主框架窗口,此时按钮遮挡住了保存等按钮。

改为在CTestView.cpp中创建button,首先在testView.cpp中创建OnCreate函数,步骤如问题及反思[3]所示。运行结果如下。

而将m_btn.Create()中的this改为GetParent(),运行结果变为

可见,按钮的位置与其父窗口有关,与创建它的代码所在的类无关。

将按钮窗口销毁,m_btn并没有销毁,因为m_btn是CTestView类的一个成员变量,其生命周期与CTestView对象一致。

课后程序

/*testview.h*/class CtestView : public CView{...private:CButton m_btn; //在定义类的成员变量时都以"m_"为前缀,表明这个变量是类的成员变量};/*testview.cpp*/int CtestView::OnCreate(LPCREATESTRUCT lpCreateStruct){if (CView::OnCreate(lpCreateStruct) == -1)return -1;/*CButton的Create函数声明:BOOL Create( LPCTSTR lpszCaption, DWORD dwStyle, const RECT& rect, CWnd* pParentWnd, UINT nID );*///m_btn.Create(_T("button"), WS_CHILD | BS_DEFPUSHBUTTON, CRect(0, 0, 100, 100), this, 123);//m_btn.ShowWindow(SW_SHOWNORMAL);//窗口显示m_btn.Create(_T("button"), WS_CHILD | BS_DEFPUSHBUTTON |WS_VISIBLE , CRect(0, 0, 100, 100), this, 123);//m_btn.Create(_T("button"), WS_CHILD | WS_VISIBLE | BS_DEFPUSHBUTTON, CRect(0, 0, 100, 100), GetParent(), 123);/*"按钮":名称; CRect(0,0,100,100):矩形区域; 123:ID号。*WS_CHILD(窗口风格):The window is a child window. A window with this style cannot have a menu bar.BS_DEFPUSHBUTTON(按钮风格):下按按钮风格WS_VISIBLE:The window is initially visible.*this指针(代表对象本身)GetParent():Call this function to get a pointer to a child window's parent window*//*C2664“BOOL CButton::Create(LPCTSTR,DWORD,const RECT &,CWnd *,UINT)”: 无法将参数 1 从“const char [7]”转换为“LPCTSTR”testE:\VCProject\Lesson3\test\test\MainFrm.cpp68解决方法:方法1、"button"改为_T("button")[2]方法2、调试>>XXX调试属性>>配置属性>>高级>>高级属性>>字符集,改为:使用多字节字符集[3]*/return 0;}

运行结果:

问题及反思

[1]如何利用vs创建MFC应用见参考文献[1]

[2]C2664 “BOOL CButton::Create(LPCTSTR,DWORD,const RECT &,CWnd *,UINT)”: 无法将参数 1 从“const char [7]”转换为“LPCTSTR”

解决方法:

方法1、"button"改为_T(“button”)

方法2、调试>>XXX调试属性>>配置属性>>高级>>高级属性>>字符集,改为:使用多字节字符集

[3]vs为一个类添加某函数的方法如下所示。第四步单击最右侧向下小三角,选择Add OnCreate。

参考文献

[1] </m0_37062716/article/details/113827243?utm_medium=distribute.pc_relevant.none-task-blog-2%7Edefault%7EBlogCommendFromMachineLearnPai2%7Edefault-1.control&depth_1-utm_source=distribute.pc_relevant.none-task-blog-2%7Edefault%7EBlogCommendFromMachineLearnPai2%7Edefault-1.control >.安装MFC,创建MFC工程文件

[2]</huijie4728/article/details/50487315> . 问题及反思[2]

[3]</feilong911hao/article/details/39231533?utm_medium=distribute.pc_relevant.none-task-blog-2%7Edefault%7EBlogCommendFromMachineLearnPai2%7Edefault-1.control&depth_1-utm_source=distribute.pc_relevant.none-task-blog-2%7Edefault%7EBlogCommendFromMachineLearnPai2%7Edefault-1.control> . 问题及反思[2]

[4]孙鑫.VC++深度详解修订版[M]. 北京:电子工业出版社, . 63-99.

如果觉得《孙鑫VC++深入详解第三章学习笔记》对你有帮助,请点赞、收藏,并留下你的观点哦!

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。