【问题标题】:How should i use getDC as a local object with autocleanup ? c++我应该如何使用 getDC 作为具有 autocleanup 的本地对象? C++
【发布时间】:2014-12-23 10:21:32
【问题描述】:

我是 C++ 新手,正在学习最好的方法。 我看到了很多关于绘画的有用示例,但我没有看到任何带有智能类的简单疙瘩 getdc。

所以我自己写了一个:

class DCObject 
{
public:
    DCObject(HWND handle)
    {
        my_handle = handle;
        my_hdc = GetDC(my_handle);
    }

    HDC GetHDC() {
        return my_hdc;
    }

    ~DCObject()
    {
        ReleaseDC(my_handle, my_hdc);
    }
private:
    HWND my_handle;
    HDC my_hdc;
};

我试图像这样获得 hdc:

HDC mydc = DCObject(dialog_item_handle).GetHDC();

这是正确的吗?要求类方法,首先不创建类对象?在这种情况下,类会发生什么?也许我确实得到了 hdc 并且它立即被释放了?(它可以工作,但它可能会对 hdc 产生未定义的行为?)

也许我应该这样做:

DCObject myhdcobj(dialog_item_handle);
 HDC mydc = myhdcobj.GetHDC();

【问题讨论】:

  • 你使用 MFC 吗?然后查看 CDC(加上派生)类!
  • 哦不,我知道 cdc(只是红色),我没有使用 mfc,还没有学过。现在简单的疙瘩win32。
  • 对您的类的一项重要改进:禁用复制(删除和/或将复制构造函数和复制赋值运算符设为私有),并支持移动(定义移动构造函数)。
  • @Cheersandhth.-Alf 我想了解你提供的内容,但我仍然不太确定该怎么做。如果可以的话,举个例子就好了。
  • @Amas:好的,我刚刚发布了一个新答案。

标签: c++ gdi


【解决方案1】:

声明

HDC mydc = DCObject(dialog_item_handle).GetHDC();

调用DCObject 构造函数,创建一个临时对象。一个临时对象(保证)在完整表达式结束时被销毁。所以在这个语句之后临时对象已经被销毁,它的析构函数调用了ReleaseDC...

解决方案:存储对象而不是句柄,

const DCObject mydc( dialog_item_handle );

我更喜欢这样写

auto const mydc = DCObject( dialog_item_handle );

...但是对于问题中提出的DCObject 类,这是不安全,因为没有处理复制/移动,因此右侧临时对象的析构函数将调用@ 987654328@…


要为常量声明启用更自然的auto 表示法,并且通常启用移动和禁用复制,请执行以下操作:

class DCObject 
{
private:
    HWND my_handle;
    HDC my_hdc;

    DCObject( DCObject const& ) = delete;
    DCObject& operator=( DCObject const& ) = delete;

public:
    auto handle() const -> HDC { return my_hdc; }

    ~DCObject()
    { ReleaseDC( my_handle, my_hdc ); }

    DCObject( const HWND handle )
        : my_handle( handle )
        , my_hdc( GetDC( handle ) )
    {}

    DCObject( DCObject&& other )
        : my_handle( other.my_handle )
        , my_hdc( other.my_hdc )
    { other.my_hdc = 0; }
};

其他类型句柄的代码——即使是设备上下文句柄的其他用途——也将非常相似。

将其表达为通用类模板所需的一个主要见解是清理所需的附加状态,例如这里的窗口句柄,可以是指定的清理函数或类似函数的对象的一部分,特别是作为 lambda 中的捕获。

如果您还没有使用 lambda,请记住这一点。

您也可以使用状态删除器的原理,而无需使用 lambdas。

【讨论】:

  • 这听起来不错,但乍一看我不太确定什么是 DCObject( DCObject const& ) = delete; DCObject& 运算符=( DCObject const& ) = 删除;因为,你能解释一下吗?为什么汽车更自然?不是说编译 HDC 更快,而不是声明我们将返回什么并使用 auto 的?
  • "= delete" 是 C++11 的一项功能。您可以省略它,使用普通声明(没有函数体)。就编译器而言,它几乎是相同的,无论如何它都必须诊断任何复制尝试,但是“= delete”会向代码的读者传达这些函数是故意没有实现的。
  • 谢谢,我有点超前了。我应该阅读更多教程。我只是在用简单的新分配查看 RAII,也许您可​​以向我指出一些有用的教程(RAII)或给我新的分配示例?我正在分配一个浮点数组
  • 44 分钟的研究,这是我的研究成果:stackoverflow.com/questions/27629781/…
  • @Arnas:哦,请使用std::vectorstd::unique_ptr<T[]> 的唯一改进是您可以避免最初将内存归零,因为它是由某些 API 函数填充的。但通常不值得。
【解决方案2】:

这就是我的做法。

#include <iostream>
#include <memory>
#include <windows.h>

template<typename T, typename D = void(*)(T &val)>
struct gdi_type
{
    typedef typename std::remove_reference<typename std::remove_pointer<T>::type>::type type;
    typedef typename std::unique_ptr<typename gdi_type<T>::type, D> unique_ptr_type;
};

template<typename T>
auto safe_gdi(const T &val)
{
    return typename gdi_type<T>::unique_ptr_type(val, [](T &t) {DeleteObject(t);});
}

auto safe_gdi(const HDC &val)
{
    return gdi_type<HDC>::unique_ptr_type(val, [](HDC &t) {DeleteDC(t);});
}

auto safe_gdi(const HWND &win, const HDC &val)
{
    auto deleter = [=](HDC &val) {ReleaseDC(win, val); val = nullptr;};
    return gdi_type<HDC, decltype(deleter)>::unique_ptr_type(val, deleter);
}

int main()
{
    HWND win = GetDesktopWindow();
    HDC DC = safe_gdi(win, GetDC(win)).get();

    HDC CDC = safe_gdi(CreateCompatibleDC(DC)).get();

    HPEN PEN = safe_gdi(CreatePen(PS_DASH, 0, 0xFF)).get();
    return 0;
}

【讨论】:

  • 嗯,这对我来说是量子物理学。乍一看,这看起来更聪明,因为(我猜)它对每种类型的 gdi 句柄或其他类型使用适当的清理功能?,在释放变量时清理它。直到现在,我仍然从未在我的生活中使用过任何这些智能指针。我不认为我会直接跳入那些^^我稍后会尝试
  • 那是有趣的风格。您不会将临时 unique_ptr 对象分配给任何东西。你只是评估他们的内容。你能确定它们不像 rValues 那样被处理并且过早地被破坏吗?我的意思是编译器可能会决定不再使用它们并在下一行销毁它们。是否有某种语言定义可以保证它们在结束时存在?
  • 啊,你是对的,你必须把它们留在身边。它们确实在下一行被销毁。
【解决方案3】:

如果您不将DCObject(dialog_item_handle) 分配给变量,您最终会得到Temporary Object

临时对象在创建它们的表达式完成时被销毁。在您的情况下,就在分配给mydc 之后。所以在mydc被赋值之后,临时对象的析构函数就会被调用,DC也会被释放。不是你想要的。

第二个版本可以工作。你的myhdcobj 只会在它超出范围时被销毁——也就是说,当你离开函数时。

【讨论】:

  • 正是我所坚持的,只是想澄清它,并将这个问题留给全世界。也许更多像我这样的菜鸟在未来对 gdi 的问题会更少。可以做得更聪明还是这里一切都好?
猜你喜欢
  • 1970-01-01
  • 2017-04-22
  • 2022-01-05
  • 2011-08-25
  • 2016-01-02
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多