当我需要一个std :: unique_ptr类型的数据成员时,我通常使用std :: unique :: reset来用一个新对象初始化这个unique_ptr。
以下是一个简化示例:
class A {
public:
void SetValue(int x) {
data_.reset(new B(x));
}
private:
std::unique_ptr<B> data_;
};
在代码审查中,一位评论者提到这是一个坏习惯,他要求我尽可能不使用重置。相反,他建议使用以下方法:
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?
答案 0 :(得分:7)
在您的示例中,无论您使用std::make_unique
还是std::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_ptrs将立即拥有各自的对象,因此如果创建另一个对象则会抛出异常。
对于例外安全以及避免直接使用新删除的一般准则,每次使用std::make_unique
都是一个好习惯。