CPaintDC dc(this);
CFont font;
dc.SelectObject(font); // why does this build?
函数CDC::SelectObject采用类型为CFont
的指针,但为什么这会提供一个对象?我遇到过这个问题,上面的代码是不可预测的,有时可能会崩溃,但并非总是如此。
答案 0 :(得分:8)
有问题的代码是有效的。它由两部分组合而编译:
CFont::operator HFONT()
。CDC::SelectObject
重载HGDIOBJ
句柄。当编译器尝试匹配dc.SelectObject(font)
的重载时,它们都不匹配。接下来,它会尝试返回operator HFONT()
的用户定义转换运算符(HFONT
)。这与未记录的重载相匹配,HGDIOBJ
(HGDIOBJ
和HFONT
都是typedef' d为void*
。
问题中发布的代码几乎也是正确的,有两个例外:
font
对象在被选入设备上下文时被销毁。虽然这会导致双删除错误(字体对象由CFont
实例和设备上下文拥有),但在传递无效句柄时,对DeleteFont
的调用会正常失败。这两个问题都不会引起不可预测的行为或间歇性崩溃。正如your answer中所解释的那样,实际代码如下所示:
CFont* pOldFont = (CFont*) dc.SelectObject(font);
这是一个展示未定义行为的错误。 dc.SelectObject(font)
会返回HGDIOBJ
(typedef' d为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
如果您愿意暂时牺牲严格的所有权语义,您可以通过使用更标准的实现来让您的生活更轻松:
CPaintDC dc(this);
CFont font;
CFont* pOldFont = dc.SelectObject(&font);
// Use dc
dc.SelectObject(pOldFont);
这是安全的,即使您提前退出,也无需恢复设备上下文。它仍然会导致对CFont
实例和设备上下文(由API优雅处理)拥有的字体对象进行双重删除。但是,它没有表现出字体泄漏,因为事情比看起来更复杂:这里涉及另一个不可见的所有者,一个由MFC控制的地图,存储临时对象(如CGdiObject::FromHandle
返回的对象,其中SelectObject(CFont*)
来电。临时对象将作为MFC空闲时间处理的一部分进行清理。
答案 1 :(得分:0)
我同意IInspectable的回答,这是我自己的概念证明:如何运作:
#include "stdafx.h"
#include <iostream>
class A {
int a_int = 6;
char* a_carp = "A derived";
public:
operator int() { return a_int; }
operator char*() { return a_carp; }
};
class B : public A {
float b_float = 5.7;
char* b_carp = "B Derived";
public:
operator float() { return b_float; }
operator char*() { return b_carp; }
};
void Print(char * text)
{
std::cout << text << std::endl;
}
int main() {
B b_obj;
// long a = b_obj;
char* c_p = b_obj;
Print(b_obj);
return 0;
}
以上代码构建,输出为“B Derived”。
它是如何运作的?
当我们使用对象代替不同类型时,该对象会查找适当的转换运算符。如果找到它,它会应用它并为你转换它。
在这种情况下,该运算符为HFONT()
。重要的是要注意操作符的实际名称无关紧要,重要的返回类型/签名。所以当它找到匹配(正确的签名)时,它会调用它。
为什么会出现不可预测的行为?
我也要清楚,因为我在OP中提到过。我发布的代码与构建问题有关,但实际代码是:
CFont* pOldFont = (CFont*) dc.SelectObject(font);
强制类型转换(CFont*)
就是问题所在。如果我们将HGDIOBJ
传递给SelectObject()
,它会返回HGDIOBJ
,但它被强制转换为CFont *
,这就是问题所以使用转换运算符没有其他问题。< / p>