1

通常 C++ 程序必须处理 C 库,这些库提供了用于分配和释放资源的免费函数。为了简化此示例,请考虑两个 C 函数,例如get_resource()free_resource()

考虑一个对象在其生命周期的某个时间点获取任何资源,并在对象被销毁或由于构造过程中的错误而未完全构造时自动释放它。

获得这种自动化的理想/简短/简单的习语是什么?一个想法如下,但它使对象不能正确地默认移动。有没有更好的方法不意味着从析构函数中释放内存或检查构造函数中的错误以进行回滚?

struct Object {
    void* my_get_resource() { // might be called from the constructor or any other point
        return get_resource();
    }

    Object() : up(&resource, &del) { 
        resource = my_get_resource();
        /* init all the stuff, but this init might fail at _many_ points */ 
    }
    //~Object() { if (resource) free_resource(resource); } // I don't like: collides with del and is not called if my_get_resource() is called from the constructor and the init process fails for some reasons

private:
    void *resource = nullptr;
    static void del(void ** ) noexcept {
        if (*resource) { free_resource(resource); }
    }
    unique_ptr < void*, decltype(&del) > up; // RAII: almost good, sadly that makes Object not moveable properly without redefining the move constructor properly


};
4

1 回答 1

4

显然你想要一个可移动的 RAII 包装器。

然后只需定义一个移动构造函数并声明一个受保护或私有的复制构造函数和复制赋值运算符。如果您不打算支持当前的 Visual C++,那么您可以将复制构造函数和复制赋值运算符声明为已删除。

这涉及在构造函数中检查错误并在析构函数中进行清理,这与您的要求不一致……

有没有更好的方法不意味着从析构函数中释放内存或检查构造函数中的错误以进行回滚?

简而言之,这些要求通常与您发布的代码所示的目标不兼容。

即使您曾经unique_ptr完成这项工作,它的工作方式也是通过检查构造函数中的错误并在析构函数中进行清理,这与您的(非常不切实际的)要求直接不一致。

以下是如何开始“手动”做事:

bool hopefully( bool const c ) { return c; }
bool throwX( string const& s ) { throw std::runtime_error( s ); }

class Resource
{
private:
    void* pResource;

    Resource( Resource const& );    // Add "= delete" if all compilers support it.
    Resource& operator=( Resource const& ); // Ditto.

public:
    void* theValue() const { return pResource; }  // Use problem-specific name.

    ~Resource()
    {
        if( pResource != 0 )
        {
            ::freeResource( pResource );
        }
    }

    Resource()
        : pResource( ::getResource() )
    {
        hopefully( pResource != 0 )
            || throwX( "Resource::<init>: getResource failed" );
    }

    Resource( Resource&& other )
        : pResource( other.pResource )
    {
        other.pResource = 0;
    }
};

您可以添加移动赋值运算符。

您可以将事物概括为Handle类模板。


免责声明:未经测试的现成代码,编译器未触及。

于 2012-12-03T00:48:36.847 回答