使用reset初始化unique_ptr是一个不好的习惯吗?(涉及IT技术)

10
当我需要一个数据成员是std::unique_ptr<A>类型时,我通常使用std::unique::reset()来初始化这个unique_ptr指向一个新对象。
以下是一个简化的例子:
class A {
 public:
  void SetValue(int x) {
    data_.reset(new B(x));
  }

 private:
  std::unique_ptr<B> data_;
};

在代码审查中,一位评审人员提到使用reset()是一种不好的习惯,他建议我尽可能不要使用。相反,他建议使用以下方法:

std::make_unique

或者使用以下模板函数:
template <typename T>
struct MakeUniqueResult {
  using scalar = std::unique_ptr<T>;
};
template <typename T, typename... Args>
typename internal::MakeUniqueResult<T>::scalar
MakeUnique(Args&&... args) {  
  return std::unique_ptr<T>(
      new T(std::forward<Args>(args)...));  
}

在上述情况下,有没有一些特殊的理由要避免使用 std::unique_ptr::reset()


5
代码的下一次编辑可能会在调用 new 和调用 reset() 之间引入某些操作。此时,原本只是一个“坏味道”变成了一个问题。 - Richard Hodges
1
当你调用 reset 时,你没有初始化。你是在改变一个已经存在的对象的状态。一个没有初始化的对象毫无意义,所以最好还是将其初始化。 - juanchopanza
2
@chris 这就是“标准”惯用语 make_unique<> 的目的。 - Richard Hodges
1
亲爱的原问题提问者:您应该将标签更改为C++14问题,因为C++11没有make_unique - TheCppZoo
1
投票重新开放。对于问题“在上述情况下,有没有一些特殊的理由避免使用std::unique_ptr::reset”,存在事实客观的答案。 - Adrian McCarthy
显示剩余8条评论
1个回答

11
在你的示例中,使用 std::make_uniquestd::unique_ptr::reset 都会产生相同的效果。
但是在更有趣的代码中,使用 std::make_unique 可以解决一些异常安全问题,这就是为什么您的代码审查人员建议将其作为习惯的原因。
考虑一下如果你尝试同时创建两个独立指针会发生什么:
Frobnicate(std::unique_ptr<Foo>(new Foo), std::unique_ptr<Bar>(new Bar(3)));

编译器需要执行一系列操作来创建这两个参数,并且在执行这些操作时有很大的灵活性。如果其中一个构造函数抛出异常,则分配给另一个构造函数的内存可能会被清理或未被清理。

但如果您使用std::make_unique

Frobnicate(std::make_unique<Foo>(), std::make_unique<Bar>(3));

如果创建另一个unique_ptr时出现异常,临时的unique_ptr将立即拥有其各自的对象,并能够清理它们。

为了提高异常安全性和避免直接使用new和delete,最好养成每次使用std::make_unique的习惯。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接