【问题标题】:Exception safety:异常安全:
【发布时间】:2014-03-16 19:51:11
【问题描述】:

我想请教一些关于异常安全的建议。特别是我一直在引用Do you (really) write exception safe code?。如果我有一个指向 Node 类型对象的指针容器,并且我要使用新的对象集合清除并重新初始化该对象容器 _nodes,那么这段代码是否是异常安全的?

std::vector<Node*> nodes;

for (int i = 0; i < 10; i++)
{
    try
    {
        // New can throw an exception. We want to make sure that if an exception is thrown any allocated memory is deleted.
        std::unique_ptr<Node> node(new Node());
        Node* n = node.get();
        nodes.push_back(n);
        node.release();
    }
    catch (std::exception& exception)
    {
        // If an exception is thrown, rollback new allocations and rethrow the exception.
        for (std::vector<Node*>::iterator it = nodes.begin(); it < nodes.end(); it++)
        {
            delete *it;
        }

        nodes.clear();
        throw exception;
    }

}

_nodes.swap(nodes);

// Delete the unused (previous) objects from the swapped container.
for (std::vector<Node*>::iterator it = nodes.begin(); it < nodes.end(); it++)
{
    delete *it;
}

我也一直在阅读 RAII,但我不知道这在我需要多态性的地方如何工作 (http://en.wikipedia.org/wiki/Polymorphism_(computer_science)#Subtyping)。

【问题讨论】:

  • std::vector&lt;std::unique_ptr&lt;Node&gt;&gt; nodes; 不是std::vector&lt;Node*&gt;
  • 哎哟!这看起来很不对劲!
  • 当没有任何迹象表明任何东西被推到向量上时,为什么要回滚?如果 new() 抛出一个异常,或者你的 Node 构造函数抛出一个异常(如果有可能它可以抛出)怎么办?
  • @PaulMcKenzie 在除第一次之外的某个迭代中可能不会发生。构造函数可能会抛出 std::bad_alloc 异常。
  • 我不明白你的 catch 块做它正在做的事情的原因。如果抛出异常,则不会将任何项目添加到向量中,但您的 catch 块假定此“坏项目”已添加到向量中。

标签: c++ deep-copy exception-safety


【解决方案1】:

这比它需要的复杂得多。我会这样开始:

std::vector<std::unique_ptr<Node>> nodes(10);    
for (auto& p : nodes)
    p.reset(new Node());

如果构造向量或分配Node 抛出,那么一切都会被自动清理。

现在,如果您明智,将_nodes 替换为std::vector&lt;std::unique_ptr&lt;Node&gt;&gt;,那么剩下的功能就是:

_nodes.swap(nodes);

不然就没那么简单了:

std::vector<Node*> nodes2;
nodes2.reserve(nodes.size());
for (auto p : nodes)
    nodes2.push_back(p.release());
_nodes.swap(nodes2);
for (auto p : nodes2)
    delete p;

假设 Node 析构函数不能抛出,这里唯一可以抛出的步骤是 reserve 调用,因此如果抛出 nodes 将被清理,因为它包含 unique_ptr 对象。之后,您可以安全地将所有权转移到nodes2,然后进行交换,然后进行清理。

我也一直在阅读 RAII,但我不知道这在我需要多态性的地方如何工作

我上面的代码依赖于 RAII,多态性是无关紧要的。上面的代码中没有不属于 RAII 类型的原始指针(_nodes 除外,您应该将其更改为 std::vector&lt;std::unique_ptr&lt;Node&gt;&gt;),因此如果抛出异常,所有内容都会被清理,无需捕获异常并进行手动清理。

【讨论】:

    猜你喜欢
    • 2016-06-16
    • 2010-12-02
    • 2016-11-20
    • 2015-09-13
    • 2014-09-28
    • 2011-03-14
    • 2016-07-05
    • 2012-12-20
    • 2012-04-23
    相关资源
    最近更新 更多