您的位置:首页 > 移动开发

C++MFC学习心得(三)——PostNcDestroy,~MainWnd(),OnDestroy(),~CWinApp()的区别。。。

2013-05-18 13:40 831 查看
今天,继续学习中,不过遇到若干内存泄漏的问题。

先说说程序构成吧,一个***的继承自CListBox的类COwnerDrawListBox。

BOOL COwnerDrawListBox::PreCreateWindow(CREATESTRUCT& cs)
{
	if(!CListBox::PreCreateWindow(cs))
		return FALSE;

	cs.style &= -(LBS_OWNERDRAWVARIABLE|LBS_SORT);
	cs.style |= LBS_OWNERDRAWFIXED;
	return TRUE;
}

void COwnerDrawListBox::MeasureItem(LPMEASUREITEMSTRUCT lpmis)
{
	lpmis->itemHeight = 32;
}
void COwnerDrawListBox::DrawItem(LPDRAWITEMSTRUCT lpdis)
{
	CDC dc;
	dc.Attach(lpdis->hDC);
	CRect rect = lpdis->rcItem;
	UINT nIndex = lpdis->itemID;

	CBrush* pBrush = new CBrush(
		::GetSysColor((lpdis->itemState&ODS_SELECTED)?COLOR_HIGHLIGHT:COLOR_WINDOW)
		);
	dc.FillRect(rect,pBrush);
	delete pBrush;
	dc.SelectObject(&FontGlob);

	if(nIndex!=(UINT)-1)
	{
		dc.SetBkMode(TRANSPARENT);
		CString str;
		GetItemData(nIndex);
		dc.TextOut(rect.left,rect.top,*((CString*)GetItemData(nIndex)));
	}
	dc.Detach();
}

int CMainWnd::OnCreate(LPCREATESTRUCT lpCreateStruct)
{
	if (CWnd::OnCreate(lpCreateStruct) == -1)
		return -1;
	FontGlob.CreatePointFont(240,"Arial");
	CClientDC dc(this);
	TEXTMETRIC tm;
	dc.GetTextMetrics(&tm);
	m_cxChar = tm.tmAveCharWidth;
	m_cyChar = tm.tmHeight + tm.tmExternalLeading;
	CRect rect(
		m_cxChar*2,
		m_cyChar*1,
		m_cxChar*40,
		m_cyChar*10);
	// TODO:  在此添加您专用的创建代码
	m_wndCNumEdit.Create(WS_CHILD|WS_VISIBLE|WS_VSCROLL|ES_MULTILINE|ES_AUTOVSCROLL|WS_BORDER|ES_WANTRETURN,rect,this,IDC_CNUMEDIT);
	rect.SetRect(m_cxChar*2,
		m_cyChar*11,
		m_cxChar*40,
		m_cyChar*20);
	m_wndCODL.Create(WS_CHILD|WS_VISIBLE|WS_VSCROLL|WS_BORDER,rect,this,IDC_CODLB);
	for(int i = 0;i<10;i++)
	{
		CString* pStr = new CString;
		pStr->Format(_T("%d"),i);
		m_wndCODL.AddString(_T(""));
		m_wndCODL.SetItemData(i,(DWORD)pStr);
	}
	return 0;
}


直接先贴代码。。。

下面是析构的东西。。。

void CMainWnd::PostNcDestroy()
{
	// TODO: 在此添加专用代码和/或调用基类
	delete this;
}
CMainWnd::~CMainWnd()
{
	CWnd::~CWnd();
}

void CMainWnd::OnDestroy()
{
	
	for(int i = 0;i<m_wndCODL.GetCount();i++)
	{
		delete (CString*)m_wndCODL.GetItemData(i);
	}
	CWnd::OnDestroy();

	// TODO: 在此处添加消息处理程序代码
}

好了,代码贴完,开始分析。。。

因为是手写,刚开始自己写的OnPaint(){},里面没有任何东西的时候,CPU直接满载,刚开始以为是控件重画导致的,可是,直到注释掉所有的代码,才发现是OnPaint的问题,最后查了一下,是OnPaint里必须至少要有一个CPaintDC的操作,原因很简单,就是没有这样的操作,系统默认没画过,ON_WM_PAINT消息一直存在,无限循环跟while(1)差不多,以后必须注意。

后来,想用这个***列表框来存储文字,比如现实你存进去的文字啦什么的。这次,当然失败了。。。

原来用的以下代码:

m_wndCODL.AddString(_T("Hello"));

然后在OnDrawItem中加入如下代码:

m_wndCODL.GetText(nIndex,str);

dc.TextOut(rect.left,rect.top,str);

这时候输出乱码,我以为是数据形式的问题,就各种换,但是不行,于是只好单步。

但不之后发现,AddString之后,AddString,对于不同的数值敏感,但是,数值一样,输出也一样。。

这时已经出现了Cwnd Object泄漏了。。。

这时我就想到可能是重画的原因了,去掉重画,直接Pass,这说明,开启重画之后,AddString加入的值并不是字符串了。查了一下,然后看了一下CListBox里的类函数,看到了SetItemData,稍微测试了一下证明了我的猜想。。。

好吧,接下来,SetItemData的参数为DWORD_PTR类型,即_w64 unsigned long,大小四字节,为什么是4字节?代码如下:

#if defined(_WIN64)
    typedef __int64 INT_PTR, *PINT_PTR;
    typedef unsigned __int64 UINT_PTR, *PUINT_PTR;

    typedef __int64 LONG_PTR, *PLONG_PTR;
    typedef unsigned __int64 ULONG_PTR, *PULONG_PTR;

    #define __int3264   __int64

#else
    typedef _W64 int INT_PTR, *PINT_PTR;
    typedef _W64 unsigned int UINT_PTR, *PUINT_PTR;

    typedef _W64 long LONG_PTR, *PLONG_PTR;
    typedef _W64 unsigned long ULONG_PTR, *PULONG_PTR;

    #define __int3264   __int32

#endif




这时候,我就明白这个DWORD_PTR的意思了,就是让你存指针用的。。。果断申请内存,CString* pStr = new CString;然后,显示,可以了。。。



泄漏问题正式开始,前文已经提到,在错误结果的时候,出现了CWnd Object泄漏,我就知道是MainWnd没有析构。。。可是怎么会出现这个呢?首先想到的是,CWinApp,因为只有CWinApp中又new CMainWnd这一句。。。然后析构,delete m_MainWnd;果断失败,不明白为什么。。。于是看了之前的例程一下,哦~~应该加个PostNcDestroy();

加上delete this,成功。。。

肯定还有失败的,那就是自己申请的那一部分内存,习惯性的加入到PostNcDestroy()中,失败,而且又出现了,CWnd Object泄漏。这下单步,看到根本没有运行delete this。。。出问题了。。。而是在删除我自己申请的内存的时候直接跳出循环的。。。感觉PostNcDestroy()只能删除一次?事实不是如此,而是,在OnDestroy之后,就已经没有控件在内存中了,所以delete出错了。。。返回值为3。。

这次,想到还有个OnDestroy 没用,在这里删除,成功了。。。这下我就想知道这几个函数之间的关系了。。。

void CMainWnd::PostNcDestroy()
{
	// TODO: 在此添加专用代码和/或调用基类
	delete this;
}
CMainWnd::~CMainWnd()
{
	CWnd::~CWnd();
}

void CMainWnd::OnDestroy()
{
	
	for(int i = 0;i<m_wndCODL.GetCount();i++)
	{
		delete (CString*)m_wndCODL.GetItemData(i);
	}
	CWnd::OnDestroy();

	// TODO: 在此处添加消息处理程序代码
}CMyApp::~CMyApp()
{
delete m_pMainWnd;
}


全加断点,然后运行,可以看出时间顺序。。。

首先,OnDestroy,最开始的,接受到ON_WN_DESTROY,调用这里,所以,这里释放一些用户内存是很好的。在这里,给用户释放的时间。

然后PostNcDestroy,释放用户视图,然后把自己析构掉。。。释放视图对象,因为这时候视图已经关掉了。

再就是,PostNcDestroy中的delete this,调用的~CMainWnd(),为什么不在这里释放用户内存?因为在窗口消失的那一瞬间(不是隐藏窗口),视图句柄都已经为0了,这个过程是在OnDestroy之前,但是一些全局内存仍然可以释放。

最后,就是~CMyApp(),释放掉进程,彻底关闭。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: