我们在调用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. linux 编译安装pureFTP

    安装openssl支持 wget -c https://www.openssl.org/source/openssl.org/source/openssl-1.1.0c.tar.gz tar -zxv ...

  2. Django学习之模拟架构页面跳转

    背景知识,需要有一定量的HTTP基础知识 在客户端游览器通过URL向服务端发送请求时,经历了两次过程.一次是URL向服务端发起请求,一次是服务端向客户端回发响应. 由图可知,客户端一共传递两个信息,一 ...

  3. Qt udp 主机和虚拟机无法互相广播

    描述: 主机和虚拟机可以ping通,port没被占用,虚拟机可以向主机广播,但是主机不能向虚拟机广播 原因: 虚拟机只配置了一个适配器,而主机有多个适配器,当虚拟机广播时,只能使用和主机连接的适配器, ...

  4. ssm中需要注意的问题

    1.在controller中需要加注解 @Controller @RequestMapping("url") @Autowired private CardService card ...

  5. 20145234黄斐《Java程序设计》第五周学习总结

    教材学习内容总结 第八章 异常处理 语法与继承架构 try.catch:try.catch代表错误的对象后做一些处理. 异常继承架构:错误会被包装为对象,这些对象均可抛出,因此设计错误对象都继承自ja ...

  6. Git项目的目录结构

     branch是分支   trunk是主干   bug修正和新功能的添加一般在branch进行 测试好了没问题了就可以合并到trunk 每隔一段时间就可以打包成一个版本放到tags 用于发布的版本一般 ...

  7. POJ2431_Expedition_KEY

    题目传送门 由题目可得,在一条路上有N个加油站,在距离终点a[i](细节)的位置上,你需要通过长度为L的路,油箱的容量是无限的,但是初始只有P点油,经过每个加油站时可以选择加b[i]的油,问最少加油几 ...

  8. 成都Uber优步司机奖励政策(3月31日)

    滴快车单单2.5倍,注册地址:http://www.udache.com/ 如何注册Uber司机(全国版最新最详细注册流程)/月入2万/不用抢单:http://www.cnblogs.com/mfry ...

  9. JS代码优化及技巧

    案例一  对象参数独立化 情景:为多个日期文本框添加日期选择器 源代码: $('#PropertySalesAdviceExchnagedDate1').datepicker({ showOn: 'b ...

  10. RAP2环境搭建整理(超详细)

    RAP2是阿里开源的接口管理平台,最近搭建了一下,将部署文档整理如下: 如果途中遇坑会在文章末尾记录下来嘻嘻 首先,确定环境是否部署好. RAP2所需的环境为: node.js 8.9.4+ mysq ...