【问题标题】:Are here any benefits from using std::unique_ptr?使用 std::unique_ptr 有什么好处吗?
【发布时间】:2021-09-10 17:59:34
【问题描述】:

我偶然发现了这样的代码:

void foo(T* bar); // Note: takes ownership of 'bar'.

foo(new T());

现在我想知道将其重构为:

void foo(T* bar); // Note: takes ownership of 'bar'.

auto tempT = std::make_unique<T>();
foo(tempT.release());
  • 是否更安全?
  • 它确实增加了关于所有权转移的更多清晰度,尽管从参数列表中调用“新”本身就已经非常清楚了。

请注意,很遗憾我无法更改 'foo' 的签名。

【问题讨论】:

  • 当前示例中的异常安全级别相同。
  • 您的想法缩小了可能发生坏事的窗口,并且可以通过unique_ptr 来避免,但这个窗口很小,所以我认为这还不够改进。
  • 如果我要重构它,我会创建一个包装函数,它接受std::unique_ptr,然后将其释放到foo。这使您可以拥有像 auto ptr = std::make_unique&lt;some_type&gt;(args); ...; wrapper(std::move(ptr)); 这样的呼叫站点。

标签: c++ exception unique-ptr


【解决方案1】:

是否更安全?

没有。然而,考虑一个稍微复杂一点的例子:

auto tempT = std::make_unique<T>();
some_operation();
foo(tempT.release());

在这种情况下,如果不使用 unique_ptr,则可能存在异常安全问题。

也就是说,更安全的是:

void foo(std::unique_ptr<T> bar);  // no need to note that takes ownership
                                   // because that's implied by the type

请注意,很遗憾我无法更改 'foo' 的签名。

然后写一个你可以控制的包装函数。

【讨论】:

  • 这与 OP 不同。您在创建指针和使用 foo 之间注入 some_operation,但 OP 的替代方法是使用 new 就地调用 foo
  • @SergeyA This is not the same example as OPs 正确。在这里重复 OPs 示例将是多余的,因为 OPs 示例已经存在问题中。 You are injecting some_operation between creation of pointer and usage in foo确实。这是比原始示例稍微复杂的部分。 OPs alternative is to use new in-place with calling foo. 在分配和调用foo 之间存在操作的更复杂的情况下,该替代方案不是一个选项。
  • 然而你的例子是做作的。您的某些操作不依赖于 tempT,因此在两者之间注入是没有意义的。一旦你开始在 some_operation 中使用 tempT,你最终会得到一个完全不同的用例。我认为,OP 的问题很明确,因为他们有两个合理的选择(就地新的,或上面的 make_unique 一行),他们在问更喜欢哪一个。您的答案似乎比 OP 需要的更笼统(即为什么 unique_ptr 比裸指针更好)。
  • @SergeyA 这篇文章的开头是对 OP 问题的回答。更人为的例子与此分开。
【解决方案2】:

不,这不是更安全的异常。

但考虑一下函数是否会更改以获取更多参数:

void foo(T* bar, T* more_bar = get_more_bar()); // Note: takes ownership of both.

foo(new T());

现在,如果在 new T() 之后评估 get_more_bar() 并抛出异常会怎样。

【讨论】:

    【解决方案3】:

    您可以考虑为 foo 编写一个包装器:

    void wrap_foo(std::unique_ptr<T> arg) {
        foo(arg.get());
        arg.release();
    }
    

    如果 foo 在不释放内存的情况下抛出异常,这将释放它。但是,如果 foo 已经是异常安全的并且在它引发异常之前释放内存,这将释放它两次。不幸的是,没有好的方法可以为此编写一个安全的包装器来检测内存是否已正确释放。

    【讨论】:

    • 一旦控制进入foo,它的工作就是释放指针。 IMO 不应假定默认情况下在这方面会破坏一项功能。
    • 然而,人们必须经常处理有缺陷的现有库并解决它们的缺陷。
    猜你喜欢
    • 1970-01-01
    • 2014-01-11
    • 1970-01-01
    • 2012-05-18
    • 2011-04-24
    • 2014-10-26
    • 1970-01-01
    • 2014-05-28
    • 2012-06-03
    相关资源
    最近更新 更多