如何在处理C库对象时实现std :: move()

aCu*_*ria 2 c++ move-semantics c++11

假设我们有以下代码

class Image
{
public:
    Image(const std::string & path)
    {
        pBitmap_ = FreeImage_Load( imageFormat, pathname.c_str() );
    }
    ~Image()
    {
        FreeImage_Unload(pBitmap_);
    }
private:
    FIBITMAP * pBitmap_;

};
Run Code Online (Sandbox Code Playgroud)

如何实现Image(Image && rhs)?在移动dtor后仍然会调用rhs,这不会产生预期的效果?我想是类似的东西

Image::Image( Image && rhs )
{
    pBitmap_ = std::move(rhs.pBitmap_);
            rhs.pBitmap_ = nullptr;
}
Run Code Online (Sandbox Code Playgroud)

然后检查dtor中的null应该做的伎俩,但有更好的方法吗?

eca*_*mur 6

解决方案是不要自己动手,而是使用该语言的库设施:

struct FiBitmapDeleter {
    void operator()(FIBITMAP *pBitmap) { FreeImage_Unload(pBitmap); }
};

class Image {
private:
    std::unique_ptr<FIBITMAP, FiBitmapDeleter> pBitmap_;
};
Run Code Online (Sandbox Code Playgroud)

现在您不需要编写析构函数,移动构造函数或移动赋值运算符.此外,您的类会自动删除其复制构造函数和复制赋值运算符,因此您无需担心这些,完成五条规则.

另一种方法是专门化default_delete,这意味着您不需要提供删除器类型unique_ptr:

namespace std {
    template<> struct default_delete<FIBITMAP> {
        void operator()(FIBITMAP *pBitmap) { FreeImage_Unload(pBitmap); }
    };
}
std::unique_ptr<FIBITMAP> pBitmap_;
Run Code Online (Sandbox Code Playgroud)

这具有全局影响,因此只有在您确信自己是程序中可能执行此专业化的唯一代码时才应该这样做.

  • 作为旁注,在实践中你仍然可能必须编写移动成员(但现在它们实现起来很简单)如果你的编译器没有自动生成它们(尽管它应该是标准的),就像MSVC一样. .但当然很好的答案. (3认同)