avo*_*avo 2 c++ constructor destructor exception raii
我从重C#背景回到C++,我继承了一些C++代码库,我认为这些代码库可能不符合最好的C++实践.
例如,我正在处理以下情况(简化):
// resource
class Resource {
HANDLE _resource = NULL;
// copying not allowed
Resource(const Resource&);
Resource& operator=(const Resource& other);
public:
Resource(std::string name) {
_resource = ::GetResource(name); if (NULL == _resource) throw "Error"; }
~Resource() {
if (_resource != NULL) { CloseHandle(_resource); _resource = NULL; };
}
operator HANDLE() const { return _resource; }
};
// resource consumer
class ResourceConsumer {
Resource _resource;
// ...
public:
void Initialize(std::string name) {
// initialize the resource
// ...
// do other things which may throw
}
}
Run Code Online (Sandbox Code Playgroud)
这里ResourceConsumer
创建一个实例Resource
并执行其他一些操作.由于某种原因(在我的控制之外),它暴露Initialize
了该方法,而不是提供非默认构造函数,显然违反了RAII模式.这是一个库代码,如果不进行重大修改,API就无法重构.
所以我的问题是,Initialize
在这种情况下如何正确编码?使用步调建筑/破坏和重新投掷是否是可接受的做法,如下所示?正如我所说,我来自C#,我只是使用try/finally
或using
模式.
void ResourceConsumer::Initialize(std::string name) {
// first destroy _resource in-place
_resource.~Resource();
// then construct it in-place
new (&_resource) Resource(name);
try {
// do other things which may throw
// ...
}
catch {
// we don't want to leave _resource initialized if anything goes wrong
_resource.~Resource();
throw;
}
}
Run Code Online (Sandbox Code Playgroud)
制作Resource
一个可移动的类型.给它移动施工/任务.然后,您的Initialize
方法可能如下所示:
void ResourceConsumer::Initialize(std::string name)
{
//Create the resource *first*.
Resource res(name);
//Move the newly-created resource into the current one.
_resource = std::move(res);
}
Run Code Online (Sandbox Code Playgroud)
请注意,在此示例中,不需要异常处理逻辑.这一切都有效.通过首先创建新资源,如果该创建抛出异常,那么我们保留先前创建的资源(如果有的话).这提供了强大的异常保证:在异常的情况下,对象的状态保持与异常之前完全相同.
请注意,不需要显式try
和catch
块.RAII就行了.
你的Resource
移动操作将是这样的:
class Resource {
public:
Resource() = default;
Resource(std::string name) : _resource(::GetResource(name))
{
if(_resource == NULL) throw "Error";
}
Resource(Resource &&res) noexcept : _resource(res._resource)
{
res._resource = NULL;
}
Resource &operator=(Resource &&res) noexcept
{
if(&res != this)
{
reset();
_resource = res._resource;
res._resource = NULL;
}
}
~Resource()
{
reset();
}
operator HANDLE() const { return _resource; }
private:
HANDLE _resource = NULL;
void reset() noexcept
{
if (_resource != NULL)
{
CloseHandle(_resource);
_resource = NULL;
}
}
};
Run Code Online (Sandbox Code Playgroud)