shared_ptr时,unique_ptr时

When shared_ptr, when unique_ptr

什么时候应该使用 shared_ptr,什么时候应该使用 unique_ptr?

例如,在此 class 而不是 node* 应该是 shared_ptr 或 unique_ptr。它取决于什么?

class node
{
private:
    node *parent;
    vector<node*> children;

    /*
     * txny
     * x - numer drzewa
     * y - numer wezla
     */
    string id;
    typeNode type; //0 - term, 1 - func

public:
    node(node* parent, string id, typeNode type);
    virtual ~node() {}

    void addChild(node* child);
    void setChildren(vector<node*> &children);
    node* getChild(int i);
    int getChildrenNumber();
    void setParent(node* parent);
    node* getParent();
    string getId();
    typeNode getType();
    void setId(string id);
}; 

编辑:

Class 树拥有节点对象。我必须写更多的文字,因为无法保存更改。

class tree
{
private:
    vector <node*> nodes;
    int depth;
    int counterNodes;
    /*
     * pxty
     * x - numer populacji (generacji)
     * y - numer drzewa
     */
    string id;
private:
    node* root;
    node* parentGeneticPoint;
public:
    tree(int depth, string id);
    tree(int depth);
    ~tree();
public:
    void initialize(typeInit type);
    void showVector();
    void show();
    Mat run();
    tree* copy();
    tree* copySubtree(int subrootI);
    tree* copyWithoutSubtree(int subrootI);

};

尽可能使用 unique_ptr(但要注意 moving/relinquishing 所有权),shared_ptr 有额外的内存和同步成本,此外,拥有一个单一的所有权位置从设计的角度来看,资源是更可取的。

在这种树结构的情况下,children 应该有 shared_ptr,parent 应该有 weak_ptr

为什么?

  • shared_ptr 也允许其他 object 指向 children(例如,如果您在某处保留一个指向子树的指针)。
  • weak_ptr 类似于 shared_ptr 但不拥有所有权。所以这里如果一个节点不再被它的parent或其他shared_ptr指向,它就会被销毁。如果它自己的 children 会有一个 shared_ptr,那么 object 将永远不会因为循环引用而被破坏。

unique_ptr应该被使用,如果只有一个object应该负责指针和它的删除。这特别意味着您不应该制作指针的副本。但是,您可以转让其所有权。

编辑:附加信息

评论显示事情比这个答案复杂。毕竟,有整本书的章节专门讨论这个主题 ;-) 三个问题将帮助您选择正确的设计:谁拥有指向的 object?会不会给外面的字指点?你想对这些指针提供什么保证?

如果您希望其他 objects(即在节点结构之外)安全地指向您的节点(即您不希望节点在外部使用时消失),您需要shared_ptr。当没有 shared_ptr 引用一个节点时,该节点将被删除。

如果相反,一个节点 "owns" 它的 children 并且独自负责它们的破坏,那么 unique_ptr 可能是一个选择。 unique_ptr::get() 可用于获取(原始)节点指针,但不能保证它们在以后仍然有效。

shared_ptr 以正则表达式解决 html 解析问题的方式解决内存管理问题。

shared_ptr 可以作为解决生命周期管理问题的一部分,但绝不是随意使用的东西。 shared_ptr 非常容易出现“放错位置”的指针或引用循环。根据我的经验,使用 shared_ptr 作为内部私有实现细节,其中包含守卫、不变量和公理,它们共同证明您无法形成循环,并且您很有可能不会出现问题。

我对 shared_ptr 的一半以上的使用包括一个“拥有”指针的位置,以及其他具有 weak_ptrs 的观察者,除了 narrow windows 当他们检查时资源仍然存在,还有理由认为 shared_ptr 不会死在狭窄的 window.

另一个很好的用途是当我遇到 copy-on-write 情况时,我有一个 nearly-immutable object 状态可以复制(存储在 shared_ptr<const T> pimpl。当发生写操作时,如果我是唯一的用户,我将其转换为 shared_ptr<T> 并修改它。否则,我将其复制到 shared_ptr<T> 并修改它。然后我将其存储回去作为两种情况下的 shared_ptr<const T>

根据我的经验,简单地散布 shared_ptrs 不可避免地会导致泄漏和资源持续时间远远超过它们应有的时间。


另一方面,您应该只使用 unique_ptr。在几乎所有情况下,make_uniqueunique_ptr 都应该替换代码中的 newdeleteunique_ptr 出错真的很难,当你出错时,通常是因为旧代码存在严重的泄漏风险,或者你之前不了解资源是如何管理的。

在新代码中,它是 no-brainer。在旧代码中,如果您需要了解所涉及的 object 的生命周期,您将不得不学习足够的知识以将所有权放入 unique_ptr 中。最大的例外是当你在做“货物崇拜”编程时(修改一个你不理解的复杂系统,看起来像系统中的其他代码,并希望它能工作,因为其他代码工作)是不可行的以这种方式管理资源。还有一些其他例外,例如 object 以复杂的方式管理自己的生命周期。

使用 unique_ptr 管理非 new 的资源有点困难,但我也觉得值得。

而有时你被迫.release把指针变成长C-stylevoid*填充call-chain.


shared_ptr 上还有一个 run-time 开销,但是 shared_ptr 的概念开销使得 object 生命周期更难理解是避免使用的真正原因它。

shared_ptr可以用来做一些花哨的事情,但是并不能解决你的问题,它是一个工具,作为综合资源管理系统的一部分,你可以使您的解决方案更简单一些。

unique_ptr 上的 run-time 开销几乎为零:它的大小与指针相同,它只是在生命周期结束时调用 delete

unique_ptr 解决了所有资源管理问题。一旦你正确使用它们就会蒸发。


在您的具体示例中,我要么将 unique_ptrs 放入 tree,并将原始指针保留在节点中。

或者,在 children 向量中保留 unique_ptrs,在树和 parent 指针中保留原始指针。

在任何一种情况下,add/remove 个节点的所有操作都应该经过 tree(将节点参数作为目标),作为 tree 的状态和 [=46] =]s 需要保持同步。


当我指出根 tree 中的节点列表不是一个好主意时,您表示有兴趣获得一个随机节点。

简单的存储每个节点中children的个数。 (当您 add/modify/delete children 必须级联到根时,这需要工作)。

添加:

node* node::nth_node( int n ) {
  if (n == 0) return this;
  --n;
  for( auto&& child:children ) {
    if (n < child->subtree_size)
      return child->nth_node(n);
    n -= child->subtree_size;
  }
  return nullptr; // n is too big
}

这会得到一个节点的第 n 个后代,假设 subtree_size 是树 node 的根(包括它本身,所以它永远不应该是 0)。

要从 tree 中获取随机节点,请创建一个从 0root->subtree_size 的随机数。即,如果 root->subtree_size3,则您的随机数为 012.

然后调用root->nth_node( that_random_number ).