我应该使用 unique_ptr 来保留班级成员吗?
Posted
技术标签:
【中文标题】我应该使用 unique_ptr 来保留班级成员吗?【英文标题】:Should I use unique_ptr to keep class' members? 【发布时间】:2016-05-25 14:07:00 【问题描述】:我有这样的代码:
class A
public:
A(void);
~A(void)
delete b;
delete c;
delete d;
// ...
private:
B* b;
C* c;
D* d;
// ...
;
//A.cpp
A(void) : b(new B()), c(new C()), d(new D()) //...
类A
拥有自己的对象b
, c
, d
...
保存这些物品的最佳方法是什么?我想,std::unique_ptr<B/C/D>
类型的使用将适合这种方式。例如,它允许不关心析构函数的仔细编写。
【问题讨论】:
你真的需要指针吗? @АлександрЛысенко:那么,你不应该把构造函数的定义放在 header 中,就像你在这里所做的那样。如果你能做到new B
,那么你就有了关于 B 的确切大小的信息。
@NathanOliver:有充分的理由不这样做。但如果他不打算,那么他也不能在标题中分配它们。
@АлександрЛысенко:我不只是在谈论析构函数。除非您有B
的定义,否则您不能调用new B
。如果你确实有定义,你可以让它们成为值而不是指针。最后,您如何定义“保存这些对象的最佳方式”?
无论如何,请使用std::unique_ptr
或std::shared_ptr
来处理这类事情。它使您的代码更安全 - 即使是您没有意识到的问题。但是,坦率地说,如果您总是 在构造函数中分配这些对象,那么您几乎可以肯定不应该使用指针。即使您需要将构造对象的实例传递给构造函数,您甚至可以避免使用指针 - 只需使用移动语义将对象移入。
【参考方案1】:
它允许不关心析构函数的仔细编写。
不止于此。
您的代码不是异常安全的。例如,如果new D()
因抛出异常而失败,则delete b
和delete c
将不会被执行并且内存会泄漏,因为如果构造函数失败,析构函数将不会被调用。 Smart pointers可以帮你避免这种情况。
将原始指针作为成员,您需要仔细实现析构函数,并复制构造函数和赋值等。请参阅What is The Rule of Three? 和Rule-of-Three becomes Rule-of-Five with C++11?。
【讨论】:
【参考方案2】:最好是按价值保留一切。如果它适合*,并且不需要隐藏**。如果它不适合或需要隐藏,第一偏好是std::unique_ptr
***,第二偏好(如果必须共享所有权)是std::shared_ptr
。并且仅作为最后的手段(我什至想不出的例子)。您实际上将拥有原始指针并自己管理生命周期,但存在内存错误和泄漏的风险。
* - 有时您希望能够按值将父对象放在堆栈上,而子对象是大数组,如果按值存储会溢出堆栈
** - 有时你不想显示子对象的真正含义(因为它们很复杂,比如 boost.fusion 适应类。那么你会想要某种形式的 PIMPL 习语:
class.hpp
struct b;
struct A std::unique_ptr<b> b_; A(); ~A();
class.cpp:
struct b ...
A::A() = default;
A::~A() = default;
*** - 使用 unique_ptr 自动管理动态分配的成员
struct A
std::unique_ptr<b> b_;
A(...):
b_(std::make_unique<b>(...))
;
【讨论】:
【参考方案3】:我觉得值得一提的是,如果你不想转让所有权,你必须使用const std::unique_ptr
。使用非常量 std:unique_ptr
允许将其转移到另一个 std:unique_ptr
。
【讨论】:
以上是关于我应该使用 unique_ptr 来保留班级成员吗?的主要内容,如果未能解决你的问题,请参考以下文章