我们在调用CWnd::GetDlgItem()函数时,MSDN告诉我们:The returned pointer may be temporary and should not be stored for later use.
中文意思就是:返回的指针可能是临时的并且最好不要保存起来放到以后用。
猜测:返回的指针既然可能是临时的,那么可能是非临时的(永久的),最好不要保存起来放到以后用(有时候可以保存起来)
源码面前,了无秘密。让我们深入MFC源代码去看个究竟。
 
先随便建立一个Dialog程序,然后在窗体上拉一个按钮,添加按钮事件,在按钮事件里写上如下代码:GetDlgItem(IDC_BUTTON1);然后给这行代码加上断点。好,我们开始进去看看,运行程序,按下按钮,程序就停在刚才的断点出,然后F11进去。
CWnd* CWnd::GetDlgItem(int nID) const
{
   ASSERT(::IsWindow(m_hWnd));
 
   if (m_pCtrlCont == NULL)
   return CWnd::FromHandle(::GetDlgItem(m_hWnd, nID));
   else
   return m_pCtrlCont->GetDlgItem(nID);
}
 
再跟踪到红色代码中去:
CWnd* PASCAL CWnd::FromHandle(HWND hWnd)
{
   CHandleMap* pMap = afxMapHWND(TRUE); //create map if not exist
   ASSERT(pMap != NULL);
   CWnd* pWnd = (CWnd*)pMap->FromHandle(hWnd);
 
#ifndef _AFX_NO_OCC_SUPPORT
   pWnd->AttachControlSite(pMap);
#endif
 
   ASSERT(pWnd == NULL || pWnd->m_hWnd == hWnd);
   return pWnd;
}
 
再一次跟踪进入红色函数里去:
CHandleMap* PASCAL afxMapHWND(BOOL bCreate)
{
   AFX_MODULE_THREAD_STATE* pState = AfxGetModuleThreadState();
   if (pState->m_pmapHWND == NULL && bCreate)
   {
   BOOL bEnable = AfxEnableMemoryTracking(FALSE);
#ifndef _AFX_PORTABLE
   _PNH pnhOldHandler = AfxSetNewHandler(&AfxCriticalNewHandler);
#endif
   pState->m_pmapHWND = new CHandleMap(RUNTIME_CLASS(CTempWnd),
   offsetof(CWnd, m_hWnd));
 
#ifndef _AFX_PORTABLE
   AfxSetNewHandler(pnhOldHandler);
#endif
   AfxEnableMemoryTracking(bEnable);
   }
   return pState->m_pmapHWND;
}
 
看一下AFX_MODULE_THREAD_STATE的定义:
// AFX_MODULE_THREAD_STATE (local to thread *and* module)
class AFX_MODULE_THREAD_STATE : public CNoTrackObject
{
public:
   AFX_MODULE_THREAD_STATE();
   virtual ~AFX_MODULE_THREAD_STATE();
 
   // current CWinThread pointer
   CWinThread* m_pCurrentWinThread;
 
   // list of CFrameWnd objects for thread
   CTypedSimpleList<CFrameWnd*> m_frameList;
 
   // temporary/permanent map state
   DWORD m_nTempMapLock; // if not 0, temp maps locked
   CHandleMap* m_pmapHWND;
   CHandleMap* m_pmapHMENU;
   CHandleMap* m_pmapHDC;
   CHandleMap* m_pmapHGDIOBJ;
   CHandleMap* m_pmapHIMAGELIST;
 
   // thread-local MFC new handler (separate from C-runtime)
   _PNH m_pfnNewHandler;
 
#ifndef _AFX_NO_SOCKET_SUPPORT
   // WinSock specific thread state
   HWND m_hSocketWindow;
#ifdef _AFXDLL
   CEmbeddedButActsLikePtr<CMapPtrToPtr> m_pmapSocketHandle;
   CEmbeddedButActsLikePtr<CMapPtrToPtr> m_pmapDeadSockets;
   CEmbeddedButActsLikePtr<CPtrList> m_plistSocketNotifications;
#else
   CMapPtrToPtr* m_pmapSocketHandle;
   CMapPtrToPtr* m_pmapDeadSockets;
   CPtrList* m_plistSocketNotifications;
#endif
#endif
};
 
看一下黄色的那一行代码,很明显,MFC说临时的map可以被锁定,先记住就可以。
看一下黄色下面的代码:
   CHandleMap* m_pmapHWND;
   CHandleMap* m_pmapHMENU;
   CHandleMap* m_pmapHDC;
   CHandleMap* m_pmapHGDIOBJ;
   CHandleMap* m_pmapHIMAGELIST;
很明显,这里放了一些临时的映射,包括HWND到CWnd,HDC到CDC的,等等。
并且,MFC除了这些临时的映射表之外,还有永久的映射表。
也就是说,GetDlgItem以及FromHandle等函数返回的CWnd以及CDC等指针是可以保存的,不管是临时的map还是永久 map中的,我们都可以安全的保存,前提是把临时的map锁定,那么不管怎么样,返回的指针可以在任何时候都是安全的。默认的,MFC是在空闲时间里把临 时map里的东西清空掉的。
 
刚才说到在空闲时间MFC会把临时的map删除掉,我们在刚才那个按钮事件里添上如下代码:
AfxGetApp()->OnIdle(1);
 
然后运行并跟踪到如下函数:
BOOL CWinApp::OnIdle(LONG lCount)
{
   if (lCount <= 0)
   {
   CWinThread::OnIdle(lCount);
 
   // call doc-template idle hook
   POSITION pos = NULL;
   if (m_pDocManager != NULL)
   pos = m_pDocManager->GetFirstDocTemplatePosition();
 
   while (pos != NULL)
   {
   CDocTemplate* pTemplate = m_pDocManager->GetNextDocTemplate(pos);
   ASSERT_KINDOF(CDocTemplate, pTemplate);
   pTemplate->OnIdle();
   }
   }
   else if (lCount == 1)
   {
   VERIFY(!CWinThread::OnIdle(lCount));
   }
   return lCount < 1; // more to do if lCount < 1
}
 
再跟踪进上面的红色函数中,整个函数如下:
BOOL CWinThread::OnIdle(LONG lCount)
{
   ASSERT_VALID(this);
 
#if defined(_DEBUG) && !defined(_AFX_NO_DEBUG_CRT)
   // check MFC's allocator (before idle)
   if (_CrtSetDbgFlag(_CRTDBG_REPORT_FLAG) & _CRTDBG_CHECK_ALWAYS_DF)
   ASSERT(AfxCheckMemory());
#endif
 
   if (lCount <= 0)
   {
   // send WM_IDLEUPDATECMDUI to the main window
   CWnd* pMainWnd = m_pMainWnd;
   if (pMainWnd != NULL && pMainWnd->m_hWnd != NULL &&
   pMainWnd->IsWindowVisible())
   {
   AfxCallWndProc(pMainWnd, pMainWnd->m_hWnd,
   WM_IDLEUPDATECMDUI, (WPARAM)TRUE, 0);
   pMainWnd->SendMessageToDescendants(WM_IDLEUPDATECMDUI,
   (WPARAM)TRUE, 0, TRUE, TRUE);
   }
   // send WM_IDLEUPDATECMDUI to all frame windows
   AFX_MODULE_THREAD_STATE* pState = _AFX_CMDTARGET_GETSTATE()->m_thread;
   CFrameWnd* pFrameWnd = pState->m_frameList;
   while (pFrameWnd != NULL)
   {
   if (pFrameWnd->m_hWnd != NULL && pFrameWnd != pMainWnd)
   {
   if (pFrameWnd->m_nShowDelay == SW_HIDE)
   pFrameWnd->ShowWindow(pFrameWnd->m_nShowDelay);
   if (pFrameWnd->IsWindowVisible() ||
   pFrameWnd->m_nShowDelay >= 0)
   {
   AfxCallWndProc(pFrameWnd, pFrameWnd->m_hWnd,
   WM_IDLEUPDATECMDUI, (WPARAM)TRUE, 0);
   pFrameWnd->SendMessageToDescendants(WM_IDLEUPDATECMDUI,
   (WPARAM)TRUE, 0, TRUE, TRUE);
   }
   if (pFrameWnd->m_nShowDelay > SW_HIDE)
   pFrameWnd->ShowWindow(pFrameWnd->m_nShowDelay);
   pFrameWnd->m_nShowDelay = -1;
   }
   pFrameWnd = pFrameWnd->m_pNextFrameWnd;
   }
   }
   else if (lCount >= 0)
   {
   AFX_MODULE_THREAD_STATE* pState = _AFX_CMDTARGET_GETSTATE()->m_thread;
   if (pState->m_nTempMapLock == 0)
   {
  // free temp maps, OLE DLLs, etc.
   AfxLockTempMaps();
   AfxUnlockTempMaps();
   }
   }
 
#if defined(_DEBUG) && !defined(_AFX_NO_DEBUG_CRT)
   // check MFC's allocator (after idle)
   if (_CrtSetDbgFlag(_CRTDBG_REPORT_FLAG) & _CRTDBG_CHECK_ALWAYS_DF)
   ASSERT(AfxCheckMemory());
#endif
 
 
   return lCount < 0; // nothing more to do if lCount >= 0
}
 
怎么样,当了老半天的间谍了,总算能看出点门道了吧?上面3行红色的代码说的很清楚,这里会把临时的map释放掉。
两个函数的代码如下:
AfxLockTempMaps();
AfxUnlockTempMaps();
 
void AFXAPI AfxLockTempMaps()
{
   AFX_MODULE_THREAD_STATE* pState = AfxGetModuleThreadState();
   ++pState->m_nTempMapLock;
}
 
BOOL AFXAPI AfxUnlockTempMaps(BOOL bDeleteTemps)
{
   AFX_MODULE_THREAD_STATE* pState = AfxGetModuleThreadState();
   if (pState->m_nTempMapLock != 0 && --pState->m_nTempMapLock == 0)
   {
   if (bDeleteTemps)
   {
   if (bDeleteTemps != -1)
   {
   // allow COM libraries to be freed
   CWinThread* pThread = AfxGetThread();
   if (pThread != NULL && pThread->m_lpfnOleTermOrFreeLib != NULL)
   (*pThread->m_lpfnOleTermOrFreeLib)(FALSE, FALSE);
   }
 
   // clean up temp objects
   pState->m_pmapHGDIOBJ->DeleteTemp();
   pState->m_pmapHDC->DeleteTemp();
   pState->m_pmapHMENU->DeleteTemp();
   pState->m_pmapHWND->DeleteTemp();
   pState->m_pmapHIMAGELIST->DeleteTemp();
   }
 
#ifndef _AFX_PORTABLE
   CWinApp* pApp = AfxGetApp();
   _AFX_THREAD_STATE* pThreadState = _afxThreadState.GetData();
   // restore safety pool after temp objects destroyed
   if (pApp != NULL &&
   (pThreadState->m_pSafetyPoolBuffer == NULL ||
   _msize(pThreadState->m_pSafetyPoolBuffer) < pApp->m_nSafetyPoolSize) &&
   pApp->m_nSafetyPoolSize != 0)
   {
   // attempt to restore the safety pool to its max size
   size_t nOldSize = 0;
   if (pThreadState->m_pSafetyPoolBuffer != NULL)
   {
   nOldSize = _msize(pThreadState->m_pSafetyPoolBuffer);
   free(pThreadState->m_pSafetyPoolBuffer);
   }
 
   // undo handler trap for the following allocation
   BOOL bEnable = AfxEnableMemoryTracking(FALSE);
   pThreadState->m_pSafetyPoolBuffer = malloc(pApp->m_nSafetyPoolSize);
   if (pThreadState->m_pSafetyPoolBuffer == NULL)
   {
   TRACE1("Warning: failed to reclaim %d bytes for memory safety pool./n",
   pApp->m_nSafetyPoolSize);
   // at least get the old buffer back
   if (nOldSize != 0)
   {
   //get it back
   pThreadState->m_pSafetyPoolBuffer = malloc(nOldSize);
   ASSERT(pThreadState->m_pSafetyPoolBuffer != NULL);
   }
   }
   AfxEnableMemoryTracking(bEnable);
   }
#endif // !_AFX_PORTABLE
   }
 
   // return TRUE if temp maps still locked
   return pState->m_nTempMapLock != 0;
}
 
看到上面黄色的代码了吧,这时候你还敢保存GetDlgItem或者FromHandle返回的指针吗?
刚才你不是说可以锁定临时map的吗?是的,可以用AfxLockTempMaps锁定,但是必须与AfxUnlockTempMaps配对使用,其内部有一个计数变量。MSDN上没这个函数,最好不要用它,否则除了什么问题我可不保证哦J
 
永久的map存放的应该就是我们显示创建的例如CWnd此类的对象了。
其实很多函数如FromHandle、FindWindow等函数都是用到了这种技术,所以MSDN必定有这样类似这样的一句话:The returned pointer may be temporary and should not be stored for later use.

FromHandle临时对象一探究竟的更多相关文章

  1. 与临时对象的斗争(上)ZZ

    C++ 是一门以效率见长的语言(虽然近来越来越多的人“不齿”谈及效率,我深以为不然,在某一次的程序编写中不对效率锱铢必较并不意味意味着我们就不应该追求更多的更好的做法).总之吧,相比起其它语言,程序员 ...

  2. 【编程篇】C++11系列之——临时对象分析

    /*C++中返回一个对象时的实现及传说中的右值——临时对象*/ 如下代码: /**********************************************/ class CStuden ...

  3. SQL Server 内置函数、临时对象、流程控制

    SQL Server 内置函数 日期时间函数 --返回当前系统日期时间 select getdate() as [datetime],sysdatetime() as [datetime2] getd ...

  4. 认识C++中的临时对象temporary object 分类: C/C++ 2015-05-11 23:20 137人阅读 评论(0) 收藏

    C++中临时对象又称无名对象.临时对象主要出现在如下场景. 1.建立一个没有命名的非堆(non-heap)对象,也就是无名对象时,会产生临时对象. Integer inte= Integer(5); ...

  5. C++中临时对象的学习笔记

    http://www.cppblog.com/besterChen/category/9573.html 所属分类: C/C++/STL/boost  在函数调用的时候,无论是参数为对象还是返回一个对 ...

  6. C++ 临时对象

    1.什么是临时对象? swap方法中,常常定义一个temp对象,这个temp对象不是临时对象,而是局部对象.这里所说的临时对象是不可见的,在原代码中是看不到的. 2.为什么会产生临时对象? a.客户期 ...

  7. 【M19】了解临时对象的来源

    1.首先,确认什么是临时对象.在swap方法中,建立一个对象temp,程序员往往把temp称为临时对象.实际上,temp是个局部对象.C++中所谓的临时对象是不可见的,产生一个non-heap对象,并 ...

  8. STL——临时对象的产生与运用

    所谓临时对象,就是一种无名对象.它的出现如果不在程序员的预期之下(例如任何pass by value操作都会引发copy操作,于是形成一个临时对象),往往造成效率上的负担.但有时候刻意制造一些临时对象 ...

  9. [转] C++中临时对象及返回值优化

    http://www.cnblogs.com/xkfz007/articles/2506022.html 什么是临时对象? C++真正的临时对象是不可见的匿名对象,不会出现在你的源码中,但是程序在运行 ...

随机推荐

  1. socketpair通信

    1.线程间通信(参考安卓源码InputTransport.cpp) #include <pthread.h> #include <sys/types.h> /* See NOT ...

  2. 批量改变图片的尺寸大小 python opencv

    我目标文件夹下有一大批图片,我要把它转变为指定尺寸大小的图片,用pthon和opencv实现的. 以上为原图片. import cv2 import os # 按指定图像大小调整尺寸 def resi ...

  3. python--re模块(正则表达式)

    RE是什么 正则 表达 式子 就是一些带有特殊含义的符号或者符号的组合 它的作用是对字符串进行过滤 在一堆字符串中找到你所关心的内容 你就需要告诉计算机你的过滤规则是什么样 通过什么方式来告诉计算机 ...

  4. 后端系统开发之gflags使用规范

    任何好用的工具如果使用不当都会带来不好的后果,gflags也是一样.我遇到过一些gflags的“坑”,还从领导和同事那里获得一些好的想法,整理成7条gflags使用规范.有意识的遵循这些规范,对项目的 ...

  5. 为什么我要放弃javaScript数据结构与算法(第四章)—— 队列

    有两种结构类似于数组,但在添加和删除元素时更加可控,它们就是栈和队列. 第四章 队列 队列数据结构 队列是遵循FIFO(First In First Out,先进先出,也称为先来先服务)原则的一组有序 ...

  6. 博科Brocade 300光纤交换机配置zone教程

    光纤交换机作为SAN网络的重要组成部分,在日常应用中非常普遍,本次将以常用的博科交换机介绍基本的配置方法. 博科300实物图: 环境描述: 如上图,四台服务器通过各自的双HBA卡连接至两台博科300光 ...

  7. pix2code开发笔记

    1.软件安装 首先需要安装Python3和pip (1) Python3 环境搭建 Window 平台安装 Python:  https://www.python.org/downloads/wind ...

  8. c++继承详解:共有(public)继承,私有继承(private)继承,保护(protected)继承

    公有继承(public)继承.私有继承(private).保护继承(protected)是常用的三种继承方式. 1.公有继承(public) 公有继承的特点是基类的公有成员和保护成员作为派生类的成员时 ...

  9. 1057: [ZJOI2007]棋盘制作

    1057: [ZJOI2007]棋盘制作 https://www.lydsy.com/JudgeOnline/problem.php?id=1057 分析: 首先对于(i+j)&1的位置0-& ...

  10. linux (rm指令) 及误删除解决

    今天在群里看见这一幕: 看到这儿,我们学习一下 这个RM指令 rm命令可以删除一个目录中的一个或多个文件或目录,也可以将某个目录及其下属的所有文件及其子目录均删除掉.对于链接文件,只是删除整个链接文件 ...