When shared_ptr, when unique_ptr

When shared_ptr, when unique_ptr

本文关键字:ptr unique when When shared      更新时间:2023-10-16

何时应该使用shared_ptr,何时使用unique_ptr?

例如,在这个类中,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 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);

};

在这种树结构的情况下,您应该将shared_ptr用于子节点,weak_ptr用于父节点。

为什么?

  • shared_ptr允许其他对象也指向子对象(例如,如果您在某处保留指向子树的指针)。
  • weak_ptrshared_ptr相似,但不占有所有权。所以在这里,如果一个节点不再被它的父节点或其他shared_ptr指向,它将被销毁。如果它自己的子对象具有shared_ptr,则该对象永远不会因为循环引用而被销毁。
如果只有一个对象负责指针及其删除,则应该使用

unique_ptr。这特别意味着你不应该复制指针。但是你可以转让它的所有权。

编辑:附加信息

评论表明问题比这个答案要复杂得多。毕竟,有整整一本书的章节专门讨论这个话题;-)三个问题应该帮助你选择正确的设计:谁拥有指向的对象?会给外界一些提示吗?对于这些指针,你想给出什么保证?

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

如果相反,一个节点"拥有"它的子节点,并对它们的破坏负责,那么unique_ptr可能是一个选择。unique_ptr::get()可用于获取指向节点的(原始)指针,但不能保证它们在以后的时间内保持有效。

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

shared_ptr可以成为终身管理问题的解决方案的一部分,但它绝不是随意使用的东西。"放错地方"是非常容易的。指针,或引用循环,使用shared_ptr。根据我的经验,使用shared_ptr作为内部私有实现细节,其中包含保护、不变量和公理,它们共同证明不能形成循环,并且您有相当大的机会不会出现问题。

我使用shared_ptr的一半以上是由一个"拥有"的位置组成的。指针,以及其他具有weak_ptr s的观察者(除了在狭窄的窗口中检查资源是否仍然存在),再加上shared_ptr不会在狭窄的窗口中死亡的理由。

另一个很好的使用块是当我有一个写时复制的情况,我有一个几乎不可变的对象状态,可以复制(存储在shared_ptr<const T> pimpl . 19中)。当写操作发生时,如果我是唯一的用户,我将其转换为shared_ptr<T>并对其进行修改。否则,我将其复制到shared_ptr<T>中并进行修改。然后我将其存储为shared_ptr<const T>

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


另一方面,应该只使用unique_ptr。几乎在任何情况下,make_uniqueunique_ptr都应该取代代码中的newdelete。让unique_ptr出错是非常非常困难的,当你出错时,通常是因为旧代码有严重的泄漏风险,或者你不理解以前是如何管理资源的。

在新代码中,这是无需动脑筋的。在旧代码中,如果您需要了解所涉及的对象的生命周期,那么无论如何,您必须学习足够的知识才能将所有权放在unique_ptr中。最大的例外是当你在做"货物崇拜"时。编程(修改一个你不理解的复杂系统,让它看起来像系统中的其他代码,并希望它能正常工作,因为其他代码正常工作)以这种方式管理资源是不可行的。也有一些其他的例外,比如对象以一种复杂的方式管理自己的生命周期。

unique_ptr管理非new 'd资源有点困难,但我也觉得值得。

有时你被迫将指针.release放入一个长c风格的void*填充的调用链中。


shared_ptr也有运行时开销,但shared_ptr的概念开销使对象生命周期更加难以理解,这是避免使用它的真正原因。

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

unique_ptr的运行时开销几乎为零:它与指针大小相同,并且在生命周期结束时只调用delete

unique_ptr解决了整个资源管理问题。一旦你使用得当,它们就会蒸发。


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

或者,将unique_ptr s保留在children向量中,原始指针保留在树和父指针中。

在任何一种情况下,所有添加/删除节点的操作都应该通过tree(为目标获取节点参数),因为treenode s的状态需要保持同步。


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

只是在每个节点中存储子节点的数量。(这需要在添加/修改/删除必须级联到根的子节点时进行工作)。

添加:

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_sizenode的根树的大小(包括它自己,所以它不应该是0)。

要从tree中获得一个随机节点,在0root->subtree_size之间创建一个随机数。例如,如果root->subtree_size3,则您的随机数是0, 12

然后调用root->nth_node( that_random_number )

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