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

时间:2018-02-16 17:57:05

标签: c++ mfc

    CPaintDC dc(this); 
    CFont font; 
    dc.SelectObject(font);  // why does this build? 

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

2 个答案:

答案 0 :(得分:8)

有问题的代码是有效的。它由两部分组合而编译:

当编译器尝试匹配dc.SelectObject(font)的重载时,它们都不匹配。接下来,它会尝试返回operator HFONT()的用户定义转换运算符(HFONT)。这与未记录的重载相匹配,HGDIOBJHGDIOBJHFONT都是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>