通过C接口分配和释放资源的正确习惯用法

时间:2012-12-02 23:22:08

标签: c++

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


};

1 个答案:

答案 0 :(得分: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类模板。


免责声明:未经测试的袖珍代码,未被编制者的手触及。