C++ std::shared_ptr:reset() 与赋值

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/31438714/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-28 13:56:47  来源:igfitidea点击:

std::shared_ptr: reset() vs. assignment

c++c++11shared-ptr

提问by AlwaysLearning

This is a basic question, but I did not find a previous post about it. The title of the following question sounds like it might be the same question as mine, but the question itself does not match the title: is it better to use shared_ptr.reset or operator =?

这是一个基本问题,但我没有找到以前的帖子。以下问题的标题听起来可能与我的问题相同,但问题本身与标题不匹配:使用 shared_ptr.reset 还是 operator = 更好?

I am confused about the purpose of the reset()member function of std::shared_ptr: what does it contribute in addition to the assignment operator?

我对reset()成员函数的用途感到困惑std::shared_ptr:除了赋值运算符之外,它还有什么作用?

To be concrete, given the definition:

具体来说,给出定义:

auto p = std::make_shared<int>(1);
  1. Are the following two lines equivalent:

    p = std::make_shared<int>(5);
    p.reset(new int(5));
    
  2. What about these:

    p = nullptr;
    p.reset();
    
  1. 以下两行是否等效:

    p = std::make_shared<int>(5);
    p.reset(new int(5));
    
  2. 这些怎么样:

    p = nullptr;
    p.reset();
    

If the two lines are equivalent in both cases, then what is the purpose of reset()?

如果这两行在两种情况下都是等价的,那么 的目的是reset()什么?



EDIT: Let me re-phrase the question to better emphasize its point. The question is: is there a case where reset()lets us achieve something that is not as easily achievable without it?

编辑:让我重新表述这个问题以更好地强调它的观点。问题是:有没有一种情况reset()可以让我们实现一些没有它就不容易实现的东西?

回答by NathanOliver

When using reset()the parameter passed to reset need not be a managed object (nor can it be); whereas with =the right hand side must be a managed object.

使用时reset()传递给 reset 的参数不需要是托管对象(也不能是);而=右手边必须是托管对象。

So these two lines give you the same end result:

所以这两行给你相同的最终结果:

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

But we cannot do:

但我们不能这样做:

p = new int(5); // compiler error no suitable overload
p.reset(std::make_shared<int>(5).get()); // uh oh undefined behavior

Without reset()you would not be able to reassign a shared pointer to a different raw pointer without creating a shared pointer and assigning it. Without =you wouldn't be able to make a shared pointer point to another shared pointer.

如果reset()没有创建共享指针并分配它,您将无法将共享指针重新分配给不同的原始指针。没有=您将无法使共享指针指向另一个共享指针。

回答by Praetorian

It's possible for resetto avoid a dynamic memory allocation in certain cases. Consider the code

这是可能的reset,以避免在某些情况下,动态内存分配。考虑代码

std::shared_ptr<int> p{new int{}};  // 1
p.reset(new int{});                 // 2

On line 1 there are 2 dynamic memory allocations happening, one for the intobject and a second one for the shared_ptr's control block that'll keep track of the number of strong/weak references to the managed object.

在第 1 行,发生了 2 个动态内存分配,一个用于int对象,第二个用于shared_ptr的控制块,它将跟踪对托管对象的强/弱引用的数量。

On line 2 there is again a dynamic memory allocation for a new intobject. Within the body of resetthe shared_ptrwill determine that there are no other strong references to the previously managed int, so it must deleteit. Since there aren't any weak references either, it could also deallocate the control block, but in this case it would be prudent for the implementation to reuse the same control block because it would otherwise have to allocate a new one anyway.

第 2 行再次为新int对象进行动态内存分配。在身体resetshared_ptr将确定有先前管理没有其它强引用int,所以它必须delete它。由于也没有任何弱引用,它也可以释放控制块,但在这种情况下,实现重用相同的控制块是谨慎的,因为否则无论如何它都必须分配一个新的。

The above behavior would not be possible if you always had to use assignment.

如果您总是不得不使用赋值,则上述行为是不可能的。

std::shared_ptr<int> p{new int{}};    // 1
p = std::shared_ptr<int>{new int{}};  // 2

In this case, the second call to the shared_ptrconstructor on line 2 has already allocated a control block, so pwill have to deallocate its own existing control block.

在这种情况下,第 2shared_ptr行对构造函数的第二次调用已经分配了一个控制块,因此p必须释放它自己现有的控制块。

回答by Alejandro

I won't include the rationale behind your first sub-question of the difference between construction via make_sharedor from a pointer, as this difference is highlighted in several different locations, including this excellent question.

我不会包括你的第一个子问题背后的基本原理,即通过make_shared或从指针构造之间的差异,因为这种差异在几个不同的位置突出显示,包括这个很好的问题

However, I think it is constructive to distinguish between using resetand operator=. The former relinquishes ownership of the resource managed by the shared_ptr, either by destroying it if the shared_ptrhappened to be the sole owner, or by decrementing the reference count. The latter implies shared ownership with another shared_ptr(unless you're move constructing).

但是,我认为区分使用reset和是有建设性的operator=。前者放弃由 管理的资源的所有权,shared_ptr如果shared_ptr碰巧是唯一所有者,则通过销毁它,或者通过减少引用计数。后者意味着与另一个人共享所有权shared_ptr(除非您正在建造)。

As I mentioned in the comments, it's important that the pointer passed in to resetnot be owned by another shared or unique pointer, because it would yield undefined behavior upon the destruction of the two independent managers - they both would attempt to deletethe resource.

正如我在评论中提到的,重要的是传入的指针reset不为另一个共享或唯一指针所有,因为它会在两个独立管理器销毁时产生未定义的行为 - 他们都会尝试delete使用资源。

One use case of resetcould be lazy initialization of a shared resource. You only want the shared_ptrto manage some resource, memory for example, if you really need it. Doing an outright allocation, such as:

一个用例reset可能是共享资源的延迟初始化。shared_ptr如果您真的需要它,您只想管理一些资源,例如内存。进行直接分配,例如:

std::shared_ptr<resource> shared_resource(new resource(/*construct a resource*/));

might be wasteful if its never actually needed. To do this with lazy initialization, something like this may apply:

如果从未真正需要它,则可能会浪费。要使用延迟初始化来做到这一点,可能适用于以下内容:

std::shared_ptr<resource> shared_resource;
void use_resource()
{
       if(!shared_resource)
       {
            shared_resource.reset(new resource(...));
       }

       shared_resource->do_foo();
}

Using resetin this case is more concise than doing a swapor assigning to a temporary shared_ptr.

reset在这种情况下使用比执行 aswap或分配给临时更简洁shared_ptr

回答by Guvante

reset()changes the managed object of an existing shared_ptr.

reset()更改现有 的托管对象shared_ptr

p = std::shared_ptr(new int(5)); and p.reset(new int(5));

p = std::shared_ptr(new int(5)); 和 p.reset(new int(5));

The former involves creating a new shared_ptrand moving it into a variable. The latter does not create a new object, it simply changes the underlying pointer in managed by the shared_ptr.

前者涉及创建一个新的shared_ptr并将其移动到一个变量中。后者不会创建新对象,它只是更改由shared_ptr.

Put another way the two are meant to be used in different cases. Assignment is for when you have a shared_ptrand resetfor when you have a raw pointer.

换句话说,这两者旨在用于不同的情况。分配是当你有一个shared_ptrreset当你有一个原始指针。

Another thing to keep in mind is that shared_ptrwas available in boost before move assignment existed and influenced the latest version heavily. Without move assignment being able to change a shared_ptrwithout making a copy is beneficial as it saves you the bookkeeping of the extra object.

要记住的另一件事是,shared_ptr在移动分配存在并严重影响最新版本之前,它在 boost 中可用。无需移动分配,shared_ptr无需复制即可更改 a是有益的,因为它可以为您节省额外对象的簿记。