谈谈MFC中的虚函数PostNcDestroy

使用MFC这种Windows界面开发框架进行开发,所做的工作通常是添加(事件、消息、虚函数)这三类东西。其中事件和消息是Windows应用开发必须可少的,而MFC库中的虚函数以及一系列宏都是其制造出来的特色。

这里要讲的是PostNcDestroy这个虚函数,它最初是在CWnd类中定义的,它的定义如下:

// for custom cleanup after WM_NCDESTROY
virtual void PostNcDestroy();

这是afxwin.h中的源码,上面一行注释,意思是说在收到WM_NCDESTROY消息后做一些定制化的清除工作。为何要在这个消息所映射的虚函数里处理呢?因为这个消息发出的时候子窗口都已经销毁了,该窗口也即将被销毁,可以在这个虚函数里做一些收尾的工作后,这个类对象已经可以被安全地析构了。可参考:WM_DESTROY 和 WM_NCDESTROY _opera321的博客-CSDN博客

它的实现如下:

void CWnd::PostNcDestroy()
{
        // default to nothing
}

这是在wincore.cpp中的源码。可以看出什么也没有干。

以下是CWnd的两个派生类CView和CFrameWnd中的虚函数PostNcDestroy实现:

// self destruction
void CView::PostNcDestroy()
{
        // default for views is to allocate them on the heap
        //  the default post-cleanup is to 'delete this'.
        //  never explicitly call 'delete' on a view
        delete this;
}

void CFrameWnd::PostNcDestroy()
{
        // default for frame windows is to allocate them on the heap
        //  the default post-cleanup is to 'delete this'.
        // never explicitly call 'delete' on a CFrameWnd, use DestroyWindow instead
        delete this;
}

可见MFC是借这个窗口销毁的时候,把在堆上创建的对象给析构了,为什么要这么做呢?

因为在应用类的InitInstance函数中,动态创建了主窗口,有一句代码:

CFrameWnd* pFrame = new CMainFrame;

随后就将这个指针赋值给m_pMainWnd:

m_pMainWnd = pFrame;

那么在什么时候,delete掉这个指针所指向的对象呢?代码里没有显式地写出来,个人觉得这是MFC代码写的不怎么好的地方。还有它定义的那些奇怪的宏,如果没有搞懂,开发者难免踩坑,这也是MFC经常被人诟病的地方。

在这个问题中,pFrame指针所指向的对象其实是在PostNcDestroy虚函数中被析构的。为什么要研究这个问题呢,因为笔者在修改CMainFrame代码时,将CMainFrame从CFrameWnd继承,改为从CWnd继承,这样创建主窗口的代码就变为:

CWnd* pFrame = new CMainFrame;

而CMainFrame的虚函数PostNcDestroy用的是CWnd的,CWnd的虚函数PostNcDestroy又是空的。这样主窗口类就失去了被析构的机会。难怪我在Debug模式下检测到内存泄漏呢!

知道了原因,解决起来就好办了。方法一:将CMainFrame的对象定义在全局静态区,注意不是堆栈。方法二就是给CMainFrame类添加虚函数PostNcDestroy,在其实现里加上一句“delete this;”就ok了。方法三:将动态创建的CMainFrame对象指针,保存为应用类的成员变量或者全局变量,在ExitInstance函数中去delete掉它也是可以的,但是delete掉m_pMainWnd是达不到效果的,因为这个时候m_pMainWnd已经为NULL了。

MFC的代码真是晦涩难懂,不理解它的情况下,很容易踩坑,出现很多意想不到的问题。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值