苗木企业网站建设源代码 园林网站源码程序 苗圃花卉网站制作源码,工业设计公司取名,wordpress怎么导入产品,初中生怎么升大专学历我是荔园微风#xff0c;作为一名在IT界整整25年的老兵#xff0c;今天我们来重新审视一下Visual Studio 2022下开发工具的MFC框架知识。
MFC中的WinMain函数是如何与MFC程序中的各个类组织在一起的呢#xff1f;MFC程序中的类是如何与WinMain函数关联起来的呢#xff1f… 我是荔园微风作为一名在IT界整整25年的老兵今天我们来重新审视一下Visual Studio 2022下开发工具的MFC框架知识。
MFC中的WinMain函数是如何与MFC程序中的各个类组织在一起的呢MFC程序中的类是如何与WinMain函数关联起来的呢
面对这个问题我们来分析一下。
双击我在我的《Visual Studio 2022的MFC框架——应用程序向导》一文中的项目例子中的类视图窗口中的CMfcApp类跳转到该类的定义文件Mfc.h中。可以发现CMfcApp派生于 CWinApp类后者表示应用程序类。
我们在类视图窗口中双击该类的构造函数就跳转到该类的源文件Mfc.cpp中。在CMfcApp构造函数处设置一个断点然后调试运行Mfc程序将发现程序首先停在CMfcApp类的构造函数处继续运行该程序。
这时程序才进入WinMain函数即停在先前我在我的《Visual Studio 2022的MFC框架——WinMain函数》一文中的WinMain函数中设置的断点处。
按我们过去在C/C编程的理解中WinMain函数是程序的入口函数。也就是说程序运行时首先应该调用的是WinMain函数那么为什么这里程序会首先调用CMfcApp类的构造函数呢
看一下CMfcApp的源文件可以发现在程序中定义了一个CMfcApp类型的全局对象theApp。代码如下。
//唯一的 CMfcApp对象CMfcApp theApp;
MFC程序的全局变量都放置在类视图窗口中的“全局函数和变量”分支下单击该分支即可看到程序当前所有的全局函数和变量。双击某个全局变量即可定位到该变量的定义处。
在这个全局对象定义处设置一个断点然后调试运行Mfc程序将发现程序执行的顺序依次是
1.theApp全局对象定义处
2.MfcApp构造函数
3.WinMain函数。
为了更好地解释这一过程我们在项目解决方案下添加一个新的“Windows控制台应用程序”项目该项目的名称为findwm。
接下来在findwm.cpp文件中输入如下所示的代码。
#include pch.h
#include iostreamusing namespace std int s100int main(){coutsendlreturn 0}
上述代码首先定义了一个int类型的全局变量s 并给它赋了一个初值100。然后在main函数中将全局变量s的值输出到标准输出cout上。
将该项目设置为启动项目 然后在main函数处设置一个断点 调试运行该程序 将会发现程序在进入main函数时 s的值已经是100了。在程序入口main函数加载时系统就已经为全局变量或全局对象分配了存储空间并为它们赋了初始值。
接下来把全局变量s换成一个全局对象看看结果如何。修改代码 新定义一个CPoint类 并定义该类的一个全局变量pt。
#includepch.h
#includeiostreamusing namespace stdclass CPoint
{
public:CPoint(){}
};CPoint pt;
void main()
{}
设置三个断点CPoint构造函数处、pt全局对象定义处和 main函数定义处。选择调试运行 main函数将会看到程序代码执行的先后顺序。
这时我们将发现findwm程序首先到达pt全局对象定义处继续运行程序程序到达CPoint类的构造函数再继续运行程序程序到达main函数处。
由此可见无论是全局变量还是全局对象程序在运行时在加载main函数之前就已经为全局变量或全局对象分配了内存空间。
对一个全局对象来说此时就会调用该对象的构造函数构造该对象并进行初始化操作。
这解释了先前创建的Mfc程序的运行顺序为什么全局变量theApp的构造函数会在 WinMain 函数之前执行。
那么为什么要定义一个全局对象theApp让它在WinMain函数之前执行呢该对象的作用是什么呢我们回到Mfc项目并将该项目设置为启动项目。
Win32 SDK应用程序的实例是由实例句柄(WinMain函数的参数hInstance)来标识的。而对MFC程序来说通过产生一个应用程序类的对象来唯一标识应用程序的实例。每一个MFC程序有且仅有一个从应用程序类(CWinApp)派生的类。每一个MFC程序实例有且仅有一个该派生类的实例化对象也就是theApp全局对象。该对象就表示了应用程序本身。
还记得子类构造函数的执行过程当一个子类在构造之前会先调用其父类的构造函数。因此theApp对象的构造函数CMfcApp 在调用之前会调用其父类CWinApp的构造函数从而就把我们程序自己创建的类与Microsoft 提供的基类关联起来了。CWinApp的构造函数完成程序运行时的一些初始化工作。
下面让我们看看CWinApp类构造函数的定义。像前面搜索“WinMain”函数那样找到Microsoft提供的CWinApp类定义的源文件appcore.cpp并在编辑环境中打开其中CWinApp构造函数的代码如下。
CWinApp::CWinApp(LPCTSTR lpszAppName)
{if (lpszAppName ! NULL)m_pszAppName_tcsdup(lpszAppName);elsem_pszAppName NULL;// initialize CWinThread stateAFX_MODULE_STATE* pModulestate _AFX_CMDTARGET_GETSTATE();ENSURE (pModulestate);AFX_MODULE_THREAD_STATE* pThreadstate pModulestate-m_thread;ENSURE(pThreadState);ASSERT(AfxGetThread() NULL);pThreadstate-m_pCurrentwinThreadthis;ASSERT (AfxGetThread() this);m_hThread ::GetCurrentThread();m_nThreadID::GetCurrentThreadId();// initialize CWinApp stateASSERT(afxCurrentWinApp NULL);pModuleState-m_pCurrentWinApp this;ASSERT (AfxGetApp () this);// in non-running state until WinMainm_hInstance NULL;m_hLangResourceDLL NULL;m_pszHelpFilePath NULL;m_pszProfileName NULL;m_pszRegistryKey NULL;m_pszExeName NULL;m_pszAppID NULL;m_pRecentFileList NULL;m_pDocManager NULL;m_atomApp m_atomSystemTopic NULL;m_lpCmdLine NULL;m_pCmdInfo NULL;m_pDataRecoveryHandler NULL;// initialize wait cursor statem_nWaitCursorCount 0;m_hcurWaitCursorRestore NULL;// initialize current printer statem_hDevMode NULL;m_hDevNames NULL;m_nNumPreviewPages 0;// initialize DAO statem_lpfnDaoTerm NULL;// other initializationm_bHelpMode FALSE;m_eHelpType afxwinHelp;m_nSafetyPoolsize 512;m_dwRestartManagerSupportFlags 0;m_nAutosaveInterval 5 * 60 * 1000;m_bTaskbarInteractionEnabled TRUE;// Detect the kind of OS:OSVERSIONINFO osvi;osvi.dwosversionInfoSize sizeof (OSVERSIONINFO);#pragma warning( disable 4996)::GetVersionEx(osvi);#pragma warning( default 4996)m_bIsWindows7 (osvi. dwMajorVersion 6) (osvi. dwMinorVersion 1)|| (osvi. dwMajorVersion 6);// Taskbar initialization:m_bComInitialized FALSE;m_pTaskbarList NULL;m_pTaskbarList3 NULL;m_bTaskBarInterfacesAvailable TRUE;
}
上述CWinApp的构造函数中有这样两行代码
pThreadState-m_pCurrentWinThread this;pModuleState-m_pCurrentWinApp this;
m_pCurrentWinThread 对象的类型是 CWinThread,该类是 CWinApp 的父类。
根据C继承性原理这个this对象代表的是子类 CMfcApp的对象即theApp。同时可以发现CWinApp的构造函数有一个LPCTSTR类型的形参lpszAppName。但是我们程序中CMfcApp的构造函数是没有参数的。如果基类的构造函数带有一个形参那么子类构造函数需要显式地调用基类带参数的构造函数。那么,为什么我们程序中的 CMfcapp构造函数没有这么做呢?
如果某个函数的参数有默认值那么在调用该函数时可以传递该参数的值也可以不传递直接使用默认值即可。
class CWinApp : public CWinThread
{DECLARE DYNAMIC (CWinApp)public://Constructorexplicit CWinApp(LPCTSTR lpszAppNameNULL);.....
可以看到CWinApp构造函数的形参确实有一个默认值(NULL)。这样在调用CWinApp类的构造函数时就不用显式地去传递这个参数的值。 作者简介荔园微风1981年生高级工程师浙大工学硕士软件工程项目主管做过程序员、软件设计师、系统架构师早期的Windows程序员Visual Studio忠实用户C/C使用者是一位在计算机界学习、拼搏、奋斗了25年的老将经历了UNIX时代、桌面WIN32时代、Web应用时代、云计算时代、手机安卓时代、大数据时代、ICT时代、AI深度学习时代、智能机器时代我不知道未来还会有什么时代只记得这一路走来充满着艰辛与收获愿同大家一起走下去充满希望的走下去。