通过C接口分配和释放资源的正确习惯用法
Correct idiom for allocating and deallocating resources via C interface
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
类模板。
免责声明:未经测试的即兴代码,编译器的手不会碰到。
相关文章:
- 复制和交换习惯用法与移动操作之间的交互
- 命名参数习惯用法和(抽象)基类
- 复制交换习惯用法-我们可以在这里使用动态强制转换操作吗
- 错误:使用复制和交换习惯用法的交换函数中"operator="的重载不明确
- 使用 PIMPL 惯用法,实现是否应始终是类的私有成员?
- C++移动分配可防止复制交换习惯用法
- 将 PIMPL 习惯用法与成员函数模板一起使用(无需预先了解所有可能的数据类型)
- 使用新线程在类似于 Scott Meyer 的单例习惯用法的实现中实例化单例是否安全?
- Pimpl习惯用法、单独的接口/实现文件和多个虚拟继承.如何
- C++迭代器"for loop"习惯用法,其步长> 1 并允许非随机访问反向迭代器
- 访问基类型数组成员(Int-to-type习惯用法)
- 基于智能指针的 CRTP 习惯用法的编译问题
- 通过C接口分配和释放资源的正确习惯用法
- 副本交换习惯用法的有效性
- 视图和控制器之间接口的习惯用法
- Boost:创建一组线程并等待所有线程的正确习惯用法是什么
- 当涉及分配器时,是否有类似于复制和交换习惯用法的东西
- 是具有复制和交换习惯用法的复制赋值运算符,建议进行自赋值检查
- C++或D:在没有动态调度的情况下解耦类的习惯用法
- 标准::rel_ops的惯用法