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
};
显然您想要一个可移动的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
类模板。
免责声明:未经测试的即兴代码,编译器的手不会碰到。