为什么CDC :: SelectObject(CFont*)接受CFont对象与指针?

zar*_*zar 6 c++ mfc

    CPaintDC dc(this); 
    CFont font; 
    dc.SelectObject(font);  // why does this build? 
Run Code Online (Sandbox Code Playgroud)

函数CDC :: SelectObject接受一个类型的指针,CFont但为什么这个构建时提供了一个对象?我遇到了这个问题,上面的代码是不可预测的,有时可能会崩溃,但并非总是如此.

IIn*_*ble 9

有问题的代码是有效的.由于两件事的组合,它编译:

当编译器尝试匹配重载时dc.SelectObject(font),它们都不匹配.接下来它会尝试operator HFONT()返回一个用户定义的转换运算符()HFONT.这与未记录的重载相匹配HGDIOBJ(两者都是HGDIOBJHFONTtypedef为void*).

问题中发布的代码几乎也是正确的,有两个例外:

  • font在仍被选择到设备上下文中时,对象将被销毁.虽然这会导致双删除错误(字体对象由CFont实例和设备上下文拥有),但DeleteFont在传递无效句柄时优雅地调用失败.
  • 先前选择到设备上下文中的字体对象丢失,从而泄露.

这两个问题都不会导致不可预测的行为或间歇性崩溃.正如您在回答中所解释的那样,真正的代码看起来像这样:

CFont* pOldFont = (CFont*) dc.SelectObject(font);
Run Code Online (Sandbox Code Playgroud)

这是一个展示未定义行为的错误.dc.SelectObject(font)返回一个HGDIOBJ(typedef'd as void*),随后将其转换为不相关的类型(CFont*).将先前选择的字体存储到设备上下文中以便以后恢复它是正确的,但代码不是.尊重所有权的实施可以是:

CPaintDC dc(this);
CFont font;
CFont oldFont;
// Transfer ownership of font to the DC, and the previously selected font into oldFont
oldFont.Attach(dc.SelectObject(font.Detach()));

// Use dc

// Transfer ownership back
font.Attach(dc.SelectObject(oldFont.Detach()));

// oldFont goes out of scope; this a no-op since it no longer owns any resources
// font goes out of scope, releasing all resources owned by it
// dc goes out of scope, releasing all resources owned by objects selected into it
Run Code Online (Sandbox Code Playgroud)

如果您愿意暂时牺牲严格的所有权语义,那么使用更标准的实现可以让您的生活更轻松:

CPaintDC dc(this); 
CFont font; 
CFont* pOldFont = dc.SelectObject(&font);

// Use dc

dc.SelectObject(pOldFont);
Run Code Online (Sandbox Code Playgroud)

这是安全的,即使您提前退出,也无需恢复设备上下文.它仍然会导致对CFont实例和设备上下文所拥有的字体对象进行双重删除(由API优雅地处理).但是,它没有出现字体泄漏,因为事情比它看起来更复杂:这里涉及另一个不可见的所有者,一个由MFC控制的地图,它存储临时对象(如从CGdiObject::FromHandle哪些SelectObject(CFont*)调用返回的对象).临时对象将作为MFC空闲时间处理的一部分进行清理.