MFC的CWinApp类是怎么调用派生类中重写的虚函数InitInstance()?

跟随我 2018-06-21 09:42:16
我们都知道MFC初始化的过程: CWinApp中的InitInstance是入口函数,一般是我们写个派生类继承他,再重写InitInstance该函数。
再定义一个全局的对象。但我没搞明白CWinApp类,他是如何调用我派生类里面的InitInstance函数。

很困惑。求大神指点

最好有个简单的例子说明一下过程。
...全文
433 14 打赏 收藏 转发到动态 举报
写回复
用AI写文章
14 条回复
切换为时间正序
请发表友善的回复…
发表回复
走好每一步 2018-06-27
  • 打赏
  • 举报
回复
当一个窗口关闭时WM_CLOSE,WM_DESTROY,WM_QUIT
死循环消息队列得收到WM_QUIT得以退出,DoModal函数得以返回。


楼主想知道MFC框架如何搭建的,应该去学下《windows程序设计》 《设计模式》c++的基本知识。
必备的阅读利器:VC助手插件
必须知道的知识:VS调试模式,MFC作为静态库的时候,可以单步阅读MFC框架所有源码!!

不要被专家误导,c++是最容易入魔的语言,因为c++的伪专家太多了!
明白c++的优势,明白c++的不足。

不要被MFC框架束缚,MFC是很古老的框架,有值得学习的地方,但是并不优雅。
走好每一步 2018-06-27
  • 打赏
  • 举报
回复

工程名cpp的 全局变量
CTestApp theApp;


appmodule.cpp
extern "C" int WINAPI
_tWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
_In_ LPTSTR lpCmdLine, int nCmdShow)
#pragma warning(suppress: 4985)
{
// call shared/exported WinMain
return AfxWinMain(hInstance, hPrevInstance, lpCmdLine, nCmdShow);
}



winmain.cpp,注意CWinApp的基类是CWinThread
int AFXAPI AfxWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
_In_ LPTSTR lpCmdLine, int nCmdShow)
{
ASSERT(hPrevInstance == NULL);

int nReturnCode = -1;
CWinThread* pThread = AfxGetThread();
CWinApp* pApp = AfxGetApp();

// AFX internal initialization
if (!AfxWinInit(hInstance, hPrevInstance, lpCmdLine, nCmdShow))
goto InitFailure;

// App global initializations (rare)
if (pApp != NULL && !pApp->InitApplication())
goto InitFailure;

// Perform specific initializations
if (!pThread->InitInstance())
{
if (pThread->m_pMainWnd != NULL)
{
TRACE(traceAppMsg, 0, "Warning: Destroying non-NULL m_pMainWnd\n");
pThread->m_pMainWnd->DestroyWindow();
}
nReturnCode = pThread->ExitInstance();
goto InitFailure;
}
nReturnCode = pThread->Run();

InitFailure:
#ifdef _DEBUG
// Check for missing AfxLockTempMap calls
if (AfxGetModuleThreadState()->m_nTempMapLock != 0)
{
TRACE(traceAppMsg, 0, "Warning: Temp map lock count non-zero (%ld).\n",
AfxGetModuleThreadState()->m_nTempMapLock);
}
AfxLockTempMaps();
AfxUnlockTempMaps(-1);
#endif

AfxWinTerm();
return nReturnCode;
}



instance是虚函数,所以会调用派生类的instance
BOOL CTestApp::InitInstance()
{
// 如果一个运行在 Windows XP 上的应用程序清单指定要
// 使用 ComCtl32.dll 版本 6 或更高版本来启用可视化方式,
//则需要 InitCommonControlsEx()。否则,将无法创建窗口。
INITCOMMONCONTROLSEX InitCtrls;
InitCtrls.dwSize = sizeof(InitCtrls);
// 将它设置为包括所有要在应用程序中使用的
// 公共控件类。
InitCtrls.dwICC = ICC_WIN95_CLASSES;
InitCommonControlsEx(&InitCtrls);

CWinApp::InitInstance();


AfxEnableControlContainer();

// 创建 shell 管理器,以防对话框包含
// 任何 shell 树视图控件或 shell 列表视图控件。
CShellManager *pShellManager = new CShellManager;

// 标准初始化
// 如果未使用这些功能并希望减小
// 最终可执行文件的大小,则应移除下列
// 不需要的特定初始化例程
// 更改用于存储设置的注册表项
// TODO: 应适当修改该字符串,
// 例如修改为公司或组织名
SetRegistryKey(_T("应用程序向导生成的本地应用程序"));

CTestDlg dlg;
m_pMainWnd = &dlg;
INT_PTR nResponse = dlg.DoModal();
if (nResponse == IDOK)
{
// TODO: 在此放置处理何时用
// “确定”来关闭对话框的代码
}
else if (nResponse == IDCANCEL)
{
// TODO: 在此放置处理何时用
// “取消”来关闭对话框的代码
}

// 删除上面创建的 shell 管理器。
if (pShellManager != NULL)
{
delete pShellManager;
}

// 由于对话框已关闭,所以将返回 FALSE 以便退出应用程序,
// 而不是启动应用程序的消息泵。
return FALSE;
}


对话框的Domodal把消息队列创建起来,UI线程跑起来,并且阻塞住winmain函数。
CTestDlg dlg;
m_pMainWnd = &dlg;
INT_PTR nResponse = dlg.DoModal();
if (nResponse == IDOK)
走好每一步 2018-06-27
  • 打赏
  • 举报
回复
擦,打个断点看下就知道了,还要问人。
schlafenhamster 2018-06-24
  • 打赏
  • 举报
回复
搜索“虚函数表”
跟随我 2018-06-23
  • 打赏
  • 举报
回复
引用 9 楼 schlafenhamster 的回复:
基类的 虚函数表 有 自己的 虚函数
1 如果 派生类 没有 重载 基类的 虚函数,那么 派生类 的 虚函数表 保留 基类的 虚函数。
2 如果 派生类 重载 了基类的 虚函数,那么派生类 的 虚函数表 存有 自己的 虚函数,
3 派生类 调用 虚函数,就是 调用 虚函数表 中的 虚函数,


大牛,能给个简易的例子,说明一下不
跟随我 2018-06-22
  • 打赏
  • 举报
回复
引用 4 楼 jiangsheng 的回复:
用基类指针调用派生类的虚函数这功能叫做多态。如果你学了C++,老师一定会讲多态的。


基类指针,需要派生类的指针进行实例化后,才能调用派生类的虚函数吧?
派生类的实例化的指针地址从何而来?
schlafenhamster 2018-06-22
  • 打赏
  • 举报
回复
实例的 虚函数表 有 自己的 虚函数,
待续_1006 2018-06-22
  • 打赏
  • 举报
回复

就这么几个步骤
zgl7903 2018-06-22
  • 打赏
  • 举报
回复
在函数中设置一个断点, F5调试启动, 停到断点后 一层层查看调用堆栈关系分析
schlafenhamster 2018-06-22
  • 打赏
  • 举报
回复
基类的 虚函数表 有 自己的 虚函数
1 如果 派生类 没有 重载 基类的 虚函数,那么 派生类 的 虚函数表 保留 基类的 虚函数。
2 如果 派生类 重载 了基类的 虚函数,那么派生类 的 虚函数表 存有 自己的 虚函数,
3 派生类 调用 虚函数,就是 调用 虚函数表 中的 虚函数,
蒋晟 2018-06-21
  • 打赏
  • 举报
回复
用基类指针调用派生类的虚函数这功能叫做多态。如果你学了C++,老师一定会讲多态的。

CWinApp派生自CWinThread,CWinThread::InitInstance是虚函数。你的派生类里是不是再加virtual无所谓,只要函数的签名和基类里的虚函数完全一样,自动重载基类的虚函数,除非中间有类在重载这个虚函数的时候加了final关键字禁止派生类再重载这个函数。
跟随我 2018-06-21
  • 打赏
  • 举报
回复
我看头文件里面,他也不是一个纯虚函数
跟随我 2018-06-21
  • 打赏
  • 举报
回复
引用 1 楼 jiangsheng 的回复:
__tmainCRTStartup首先调用_initterm来调用全局对象的构造函数,然后CWinApp::CWinApp会把自己的地址存到一个全局的指针里面去(所以不能有两个全局的CWinApp对象)
然后调用CRT提供的WinMain ,这个会调用AfxWinMain,里面会调用这个全局的指针。至于你的InitInstance调用是因为InitInstance被声明成为了虚函数。


#include<afxwin.h>
class CMyApp : public CWinApp
{
virtual BOOL InitInstance()
{

return TRUE;
}
};
CMyApp theApp;

就因为声明成了虚函数,CWinApp就能调用派生类里面实现的虚函数?
蒋晟 2018-06-21
  • 打赏
  • 举报
回复
__tmainCRTStartup首先调用_initterm来调用全局对象的构造函数,然后CWinApp::CWinApp会把自己的地址存到一个全局的指针里面去(所以不能有两个全局的CWinApp对象)
然后调用CRT提供的WinMain ,这个会调用AfxWinMain,里面会调用这个全局的指针。至于你的InitInstance调用是因为InitInstance被声明成为了虚函数。
FC常用及其成员函数 CRuntimeClass结构 在CRuntimeClass结构定义了名、对象所占存储空间的大小、的版本号等成员变量及动态创建对象、派生关系判断等成员函数。每一个从CObject派生的都有一个CRuntimeClass结构同它关联,以便完成在运行时得到对象的信息或基的信息。 要使用CRuntimeClass结构,必须结合使用RUNTIME_CLASS()宏和其他有关运行时型识别的MFC宏。 CCmdTarget (1)消息发送 MFC应用程序为每个CCmdTarget派生创建一个称为消息映射表的静态数据结构,可将消息映射到对象所对应的消息处理函数上。 (2)设置光标 BeginWaitCursor() 将光标改为沙漏形状; EndWaitCursor() 将光标改回到之前的形状; RestoreWaitCursor()用于将光标还原为等待状态。 (3)支持自动化 CCmdTarget支持程序通过COM接口进行交互操作,自动翻译COM接口的方法。 CWinThread 由CCmdTarget派生,主要工作是创建和处理消息循环。 CWinApp 从CWinThread派生,成员函数InitApplication()、InitInstance()、Run()。 在InitInstance()函数,创建了一个单文档模板或多文档模板(CDocTemplate)的对象,并且在文档模板的构造函数,系统定义的宏RUNTIME_CLASS创建了文档对象,框架窗口对象和视图对象. 在MFC应用程序有且仅有一个CWinApp派生的对象,代表程序运行的主线程,代表应用程序本身。 CWnd 由CCmdTarget直接派生,是MFC最基本的GUI对象。公共变量m_hWnd用于存放供API函数调用的窗口句柄。 CframeWnd 从CWnd派生而来,主要用来掌管一个窗口。其对象是一个框架窗口,包括边界、标题栏、菜单、最大化按钮、最小化按钮和一个激活的视图。常用成员函数: GetActiveDocument():得到当前文档的指针。 GetActiveView(): 得到当前视图的指针。 SetActiveView(): 激活一个视图。 GetTitle(): 得到框架窗口的标题。 SetTitle(): 设置框架窗口的标题。 SetMessageText(): 设置状态栏文本。 CDocument 从CCmdTarget派生,作为用户文档的基,代表了用户存储或打开一个文件。主要功能是把对数据的处理从对用户的界面处理分离出来,同时提供一个与视图交互的接口。常用的成员函数有: OnNewDocument(): 建立新文档。 OnOpenDocument(): 打开一个文档。 OnCloseDocument(): 关闭文档。 OnSaveDocument(): 保存文档。 UpdateAllView(): 通知所有视图文档被修改。 SaveModified(): 设置文档修改标志。 CView 从CWnd派生而来,是MFC视图和用户视图的基。CWnd::Invalidate()或CWnd::InvalidateRect()可以刷新视图。常用函数有: GetDocument(): 视图对象访问文档对象的数据的. OnDraw(): 这个函数有一个指向CDC的指针参数, 通过它可能直接调用CDC上显示数据和图形. 在应用程序窗口出现在及大小发生变化时, 系统将自动调用OnDraw函数 OnInitialUpdate(): 作一些初始化工作. 程序员的主要工作 (1) 重写WinApp派生虚函数InitInstance.在这个函数,按自己的需要创建和显示窗口. (2) 在CDocument的派生,声明程序所需的数据和对这些数据进行必要操作的接口函数. (3) 在CViwe派生编写处理消息的代码.如果在消息处理需要文档的数据,应该调用的成员函数GetDocument来获取文档对象,然后通过文档对象的接口函数对文档的数据进行操作. (4) 在CViwe派生的OnDraw函数编写窗口重绘时的代码. Gilbert觉得以上是很大的, 下面介绍一些小: CRect 矩形,拥有四个成员变量:top, left, bottom, right。分别表是左上角和右下角的坐标。可以通过以下的方法构造: CRect( int l, int t, int r, int b ); 指明四个坐标 CRect( const RECT& srcRect ); 由RECT结构构造 CRect( LPCRECT lpSrcRect ); 由RECT结构构造 CRect( POINT point, SIZE size ); 有左上角坐标和尺寸构造 CRect( POINT topLeft, POINT bottomRight ); 有两点坐标构造 它的几个成员函数: int Width( ) const; 得到宽度 int Height( ) const; 得到高度 CSize Size( ) const; 得到尺寸 CPoint& TopLeft( ); 得到左上角坐标 CPoint& BottomRight( ); 得到右下角坐标 CPoint CenterPoint( ) const; 得当心坐标 此外矩形可以和点(CPoint)相加进行位移,和另一个矩形相加得到“并”操作后的矩形。 CPoint 点的坐标,有两个成员变量:x, y。可以和另一个点相加。 CString 表示可变长度的字符串。使用CString可不指明内存大小,CString会根据需要自行分配。几个成员函数: GetLength 得到字符串长度 operator + 相当于strcat Compare 比较 CompareNoCase 不区分大小写比较 MakeUpper 改为小写 MakeLower 改为大写

16,472

社区成员

发帖
与我相关
我的任务
社区描述
VC/MFC相关问题讨论
社区管理员
  • 基础类社区
  • Web++
  • encoderlee
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告

        VC/MFC社区版块或许是CSDN最“古老”的版块了,记忆之中,与CSDN的年龄几乎差不多。随着时间的推移,MFC技术渐渐的偏离了开发主流,若干年之后的今天,当我们面对着微软的这个经典之笔,内心充满着敬意,那些曾经的记忆,可以说代表着二十年前曾经的辉煌……
        向经典致敬,或许是老一代程序员内心里面难以释怀的感受。互联网大行其道的今天,我们期待着MFC技术能够恢复其曾经的辉煌,或许这个期待会永远成为一种“梦想”,或许一切皆有可能……
        我们希望这个版块可以很好的适配Web时代,期待更好的互联网技术能够使得MFC技术框架得以重现活力,……

试试用AI创作助手写篇文章吧