我应该使用 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_ptrstd::shared_ptr 来处理这类事情。它使您的代码更安全 - 即使是您没有意识到的问题。但是,坦率地说,如果您总是 在构造函数中分配这些对象,那么您几乎可以肯定不应该使用指针。即使您需要将构造对象的实例传递给构造函数,您甚至可以避免使用指针 - 只需使用移动语义将对象移入。 【参考方案1】:

它允许不关心析构函数的仔细编写。

不止于此。

    您的代码不是异常安全的。例如,如果new D() 因抛出异常而失败,则delete bdelete 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 来保留班级成员吗?的主要内容,如果未能解决你的问题,请参考以下文章

Proguard 不会保留班级成员的枚举

如何初始化一个unique_ptr

我应该使用 unique_ptr 作为字符串吗?

如何在构造函数中为成员 unique_ptr 赋予默认值? [复制]

vector<unique_ptr<T> > 作为基类成员

我可以将班级成员的哈希码用于班级吗?