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

第三章

3.1创建MFC AppWizard

如何利用vs2019创建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 WinMain
	return 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();

// Attributes
public:
	HWND m_hWnd;            // must be first data member
    ......
    };


/*lesson3:\\wainmain.cpp*/
int WINAPI WinMain(  HINSTANCE hInstance,      // handle to current instance
  HINSTANCE hPrevInstance,  // handle to previous instance
  LPSTR lpCmdLine,          // command line
  int 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”	test	E:\VCProject\Lesson3\test\test\MainFrm.cpp	68

	解决方法:
	方法1、"button"改为_T("button")[2]
	方法2、调试>>XXX调试属性>>配置属性>>高级>>高级属性>>字符集,改为:使用多字节字符集[3]
	*/
	return 0;
}

运行结果:
在这里插入图片描述

问题及反思

[1]如何利用vs2019创建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]vs2019为一个类添加某函数的方法如下所示。第四步单击最右侧向下小三角,选择Add OnCreate。
在这里插入图片描述

参考文献

[1] <https://blog.csdn.net/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]<https://blog.csdn.net/huijie4728/article/details/50487315> . 问题及反思[2]
[3]<https://blog.csdn.net/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]. 北京:电子工业出版社, 2012. 63-99.


  • 2
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
孙鑫vc是一种特殊的混合编程语言,它结合了C语言和Verilog语言的特点。在深入详解孙鑫vc代码之前,我们先了解一下它的一些特性。 首先,孙鑫vc具有高度的可定制性。用户可以根据自己的需求选择C语言和Verilog语言中的特性来编写代码。这种灵活性使得孙鑫vc可以适用于不同的应用领域。 其次,孙鑫vc支持并行计算。它提供了一种简单而有效的方式来利用硬件资源进行并行计算,提高程序的执行效率。 另外,孙鑫vc还具有强大的调试功能。它能够在运行时对代码进行监控和调试,帮助开发者快速定位问题并进行修复。 深入详解孙鑫vc代码包括以下几个方面: 首先,我们可以从代码的结构和组织方式入手。孙鑫vc代码一般由多个模块组成,每个模块包含了各自的功能和接口。 其次,我们需要了解代码中使用的变量和数据类型。在孙鑫vc中,可以使用C语言和Verilog语言中的数据类型,如整型、浮点型等。了解这些数据类型的使用方法和限制对理解代码非常重要。 然后,我们需要分析代码中的控制流和算法。这包括了代码中的条件语句、循环语句等,以及算法的实现细节。通过对控制流和算法的分析,我们可以更好地理解代码的逻辑和实现原理。 最后,我们还需要关注代码中的接口和数据传输方式。在孙鑫vc中,模块之间通过接口进行数据的传递和交互。了解接口的定义和使用方式对于理解代码的功能和模块之间的关系非常重要。 综上所述,深入详解孙鑫vc代码需要从代码的结构和组织方式、变量和数据类型、控制流和算法、接口和数据传输方式等多个方面进行分析和理解。通过对这些方面的研究,我们可以更好地理解孙鑫vc代码,并且能够对代码进行修改和优化。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

贾宝玉怒撞不周山

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值