为何CDC::SelectObject(CFont*)可接受CFont对象而非指针?偶发崩溃解析
关于CDC::SelectObject的两个疑问解答
这问题戳中了MFC里一个很容易踩的隐式转换坑,我给你一步步理清楚:
疑问一:为什么CDC::SelectObject(CFont*)能接受CFont对象而非指针?
其实你误解了函数调用的重载匹配——CDC::SelectObject根本不是在接受CFont对象,而是MFC的CFont类重载了operator HFONT()运算符,它会把CFont对象隐式转换成底层的GDI字体句柄HFONT,而CDC::SelectObject刚好有另一个重载:HGDIOBJ SelectObject(HGDIOBJ hObject)(HFONT是HGDIOBJ的子类)。
所以当你传入CFont对象时,编译器会自动触发这个隐式转换,把对象转成HFONT,然后匹配到接受HGDIOBJ的重载,而不是你以为的CFont*版本。这就造成了“好像接受对象”的错觉。
疑问二:传入对象能编译但偶发崩溃的原因?
这就是隐式转换带来的危险了:
- 当你调用接受
HGDIOBJ的重载时,MFC不会做任何对象层面的关联跟踪——它只认GDI句柄,不知道这个句柄背后绑定的CFont对象。 - 一旦CFont对象的生命周期结束(比如出了局部作用域被销毁),它会自动释放关联的GDI字体句柄。这时候CDC里选入的还是那个已经失效的句柄,后续对CDC的绘图操作就会触发未定义行为——有时候对象还没被销毁就没事,有时候刚好对象被释放就崩溃,所以表现为偶发崩溃。
- 而如果用
CFont*的重载(传入&myFont),MFC会内部记录当前选入的CFont对象,确保句柄和对象的生命周期同步,同时返回旧的CFont指针,方便你后续恢复字体,避免句柄失效。
举个错误和正确的代码对比:
错误写法(偶发崩溃风险)
void DrawSomething(CDC& dc) { CFont tempFont; tempFont.CreatePointFont(120, _T("微软雅黑")); dc.SelectObject(tempFont); // 隐式转HFONT,调用HGDIOBJ重载 dc.TextOut(10,10,_T("崩溃风险极高")); } // tempFont销毁,释放HFONT句柄,后续dc操作会踩失效句柄
正确写法(安全)
void DrawSomething(CDC& dc) { CFont tempFont; tempFont.CreatePointFont(120, _T("微软雅黑")); CFont* pOldFont = dc.SelectObject(&tempFont); // 调用CFont*重载 dc.TextOut(10,10,_T("安全绘图")); dc.SelectObject(pOldFont); // 恢复旧字体,避免句柄泄漏/失效 }
内容的提问来源于stack exchange,提问作者zar
相关产品推荐
相关产品推荐

