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_ptr
s 的观察者,除了 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_ptr
s 不可避免地会导致泄漏和资源持续时间远远超过它们应有的时间。
另一方面,您应该只使用 unique_ptr
。在几乎所有情况下,make_unique
和 unique_ptr
都应该替换代码中的 new
和 delete
。 unique_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_ptr
s 放入 tree
,并将原始指针保留在节点中。
或者,在 children
向量中保留 unique_ptr
s,在树和 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
中获取随机节点,请创建一个从 0
到 root->subtree_size
的随机数。即,如果 root->subtree_size
为 3
,则您的随机数为 0
、1
或 2
.
然后调用root->nth_node( that_random_number )
.
什么时候应该使用 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_ptr
s 的观察者,除了 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_ptr
s 不可避免地会导致泄漏和资源持续时间远远超过它们应有的时间。
另一方面,您应该只使用 unique_ptr
。在几乎所有情况下,make_unique
和 unique_ptr
都应该替换代码中的 new
和 delete
。 unique_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_ptr
s 放入 tree
,并将原始指针保留在节点中。
或者,在 children
向量中保留 unique_ptr
s,在树和 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
中获取随机节点,请创建一个从 0
到 root->subtree_size
的随机数。即,如果 root->subtree_size
为 3
,则您的随机数为 0
、1
或 2
.
然后调用root->nth_node( that_random_number )
.