这是一个基本问题,但我没有找到关于它的先前帖子。以下问题的标题听起来可能与我的问题相同,但问题本身与标题不符:is it better to use shared_ptr.reset or operator =?
我对reset()
的{{1}}成员函数的目的感到困惑:除了赋值运算符之外,它还有什么作用呢?
具体而言,给定定义:
std::shared_ptr
以下两行是否相同:
auto p = std::make_shared<int>(1);
这些怎么样:
p = std::make_shared<int>(5);
p.reset(new int(5));
如果这两行在两种情况下都是等效的,那么p = nullptr;
p.reset();
的目的是什么?
reset()
让我们实现没有它就不容易实现的东西的情况?
答案 0 :(得分:23)
使用reset()
时,传递给reset的参数不必是托管对象(也不是);而对于=
,右侧必须是托管对象。
所以这两行给出了相同的最终结果:
p = std::make_shared<int>(5); // assign to a newly created shared pointer
p.reset(new int(5)); // take control of a newly created pointer
但我们做不到:
p = new int(5); // compiler error no suitable overload
p.reset(std::make_shared<int>(5).get()); // uh oh undefined behavior
如果没有reset()
,您将无法在不创建共享指针和分配指针的情况下将共享指针重新分配给其他原始指针。如果没有=
,您将无法使共享指针指向另一个共享指针。
答案 1 :(得分:6)
reset
可能会在某些情况下避免动态内存分配。考虑代码
std::shared_ptr<int> p{new int{}}; // 1
p.reset(new int{}); // 2
在第1行,有2个动态内存分配,一个用于int
对象,另一个用于shared_ptr
的控制块,用于跟踪强弱的数量对托管对象的引用。
在第2行,再次为新的int
对象分配动态内存。在reset
的正文中shared_ptr
将确定没有其他强引用以前管理的int
,因此它必须delete
。由于也没有任何弱引用,它也可以解除分配控制块,但在这种情况下,重复使用相同的控制块将是谨慎的,因为否则它将不得不分配一个新的控制块。
如果您总是必须使用作业,则无法执行上述行为。
std::shared_ptr<int> p{new int{}}; // 1
p = std::shared_ptr<int>{new int{}}; // 2
在这种情况下,第2行对shared_ptr
构造函数的第二次调用已经分配了一个控制块,因此p
将不得不释放自己现有的控制块。
答案 2 :(得分:2)
我不会通过make_shared
或指针包含构造之间差异的第一个子问题背后的基本原理,因为这个差异在几个不同的位置突出显示,包括this excellent question 。
但是,我认为区分使用reset
和operator=
是有建设性的。前者放弃shared_ptr
管理的资源的所有权,或者通过在shared_ptr
恰好是唯一所有者时销毁它,或者通过递减引用计数来放弃它。后者意味着与另一个shared_ptr
共享所有权(除非您正在移动构建)。
正如我在评论中提到的那样,传递给reset
的指针不是由另一个共享或唯一指针所拥有是非常重要的,因为它会在两个独立指针被破坏时产生未定义的行为。经理 - 他们都会尝试delete
资源。
reset
的一个用例可能是共享资源的延迟初始化。您只希望shared_ptr
管理一些资源,例如内存,如果您真的需要它。进行彻底的分配,例如:
std::shared_ptr<resource> shared_resource(new resource(/*construct a resource*/));
如果从未真正需要,可能会浪费。要使用延迟初始化执行此操作,可能会应用以下内容:
std::shared_ptr<resource> shared_resource;
void use_resource()
{
if(!shared_resource)
{
shared_resource.reset(new resource(...));
}
shared_resource->do_foo();
}
在这种情况下使用reset
比执行swap
或分配给临时shared_ptr
更简洁。
答案 3 :(得分:0)
reset()
更改现有shared_ptr
的托管对象。
p = std :: shared_ptr(new int(5));和p.reset(new int(5));
前者涉及创建新的shared_ptr
并将其移动到变量中。后者不会创建新对象,它只是更改shared_ptr
所管理的基础指针。
换句话说,两者意味着在不同情况下使用。分配是指当你有一个原始指针时有shared_ptr
和reset
的时候。
要记住的另一件事是shared_ptr
在移动分配存在之前可用于增强并大大影响了最新版本。没有移动分配就可以在不制作副本的情况下更改shared_ptr
,这样可以节省额外对象的簿记。