C++ 我们应该通过引用还是通过值传递 shared_ptr ?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/3310737/
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 12:36:08  来源:igfitidea点击:

Should we pass a shared_ptr by reference or by value?

c++c++11boostshared-ptr

提问by Danvil

When a function takes a shared_ptr(from boost or C++11 STL), are you passing it:

当一个函数采用shared_ptr(来自 boost 或 C++11 STL)时,你是否通过了它:

  • by const reference: void foo(const shared_ptr<T>& p)

  • or by value: void foo(shared_ptr<T> p)?

  • 通过常量引用: void foo(const shared_ptr<T>& p)

  • 或按价值:void foo(shared_ptr<T> p)?

I would prefer the first method because I suspect it would be faster. But is this really worth it or are there any additional issues?

我更喜欢第一种方法,因为我怀疑它会更快。但这真的值得吗,或者还有其他问题吗?

Could you please give the reasons for your choice or if the case, why you think it does not matter.

能否请您说明您选择的理由,或者如果是这样,您认为这无关紧要的原因是什么。

采纳答案by mloskot

This question has been discussed and answered by Scott, Andrei and Herb during Ask Us Anythingsession at C++ and Beyond 2011. Watch from 4:34 on shared_ptrperformance and correctness.

Scott、Andrei 和 Herb在C++ and Beyond 2011 的Ask Us Anything会议上讨论并回答了这个问题。从 4:34开始观看性能和正确性shared_ptr

Shortly, there is no reason to pass by value, unless the goal is to share ownership of an object(eg. between different data structures, or between different threads).

简而言之,没有理由按值传递,除非目标是共享对象的所有权(例如,在不同的数据结构之间,或在不同的线程之间)。

Unless you can move-optimise it as explained by Scott Meyers in the talk video linked above, but that is related to actual version of C++ you can use.

除非您可以像 Scott Meyers 在上面链接的谈话视频中解释的那样对其进行移动优化,但这与您可以使用的 C++ 的实际版本有关。

A major update to this discussion has happened during GoingNative 2012conference's Interactive Panel: Ask Us Anything!which is worth watching, especially from 22:50.

GoingNative 2012会议的互动小组中,对这一讨论进行了重大更新:向我们提问!值得一看,尤其是从22:50 开始

回答by acel

Here's Herb Sutter's take

这是赫伯·萨特 (Herb Sutter) 的看法

Guideline: Don't pass a smart pointer as a function parameter unless you want to use or manipulate the smart pointer itself, such as to share or transfer ownership.

Guideline: Express that a function will store and share ownership of a heap object using a by-value shared_ptr parameter.

Guideline: Use a non-const shared_ptr& parameter only to modify the shared_ptr. Use a const shared_ptr& as a parameter only if you're not sure whether or not you'll take a copy and share ownership; otherwise use widget* instead (or if not nullable, a widget&).

指南:不要将智能指针作为函数参数传递,除非您想使用或操作智能指针本身,例如共享或转移所有权。

准则:表示函数将使用按值 shared_ptr 参数存储和共享堆对象的所有权。

指南:使用非常量 shared_ptr& 参数仅用于修改 shared_ptr。仅当您不确定是否要复制并共享所有权时,才使用 const shared_ptr& 作为参数;否则使用widget* 代替(或者如果不可为空,则使用widget&)。

回答by Evan Teran

Personally I would use a constreference. There is no need to increment the reference count just to decrement it again for the sake of a function call.

我个人会使用const参考。没有必要为了函数调用而增加引用计数只是为了再次减少它。

回答by Nikolai Fetissov

Pass by constreference, it's faster. If you need to store it, say in some container, the ref. count will be auto-magically incremented by the copy operation.

通过const引用传递,它更快。如果你需要储存它,比如在某个容器中,ref. count 将通过复制操作自动增加。

回答by tcb

I ran the code below, once with footaking the shared_ptrby const&and again with footaking the shared_ptrby value.

我跑到下面的代码,一旦与foo服用shared_ptrconst&,并再次fooshared_ptr的值。

void foo(const std::shared_ptr<int>& p)
{
    static int x = 0;
    *p = ++x;
}

int main()
{
    auto p = std::make_shared<int>();
    auto start = clock();
    for (int i = 0; i < 10000000; ++i)
    {
        foo(p);
    }    
    std::cout << "Took " << clock() - start << " ms" << std::endl;
}

Using VS2015, x86 release build, on my intel core 2 quad (2.4GHz) processor

在我的英特尔核心 2 四核 (2.4GHz) 处理器上使用 VS2015、x86 版本构建

const shared_ptr&     - 10ms  
shared_ptr            - 281ms 

The copy by value version was an order of magnitude slower.
If you are calling a function synchronously from the current thread, prefer the const&version.

按值复制的版本慢了一个数量级。
如果您从当前线程同步调用函数,请首选const&版本。

回答by Cookie

Since C++11 you should take it by value over const&more often than you might think.

从 C++11 开始,你应该比你想象的更频繁地使用const& 的值

If you are taking the std::shared_ptr (rather than the underlying type T), then you are doing so because you want to do something with it.

如果您正在使用 std::shared_ptr(而不是基础类型 T),那么您这样做是因为您想用它做一些事情。

If you would like to copy itsomewhere, it makes more sense to take it by copy, and std::move it internally, rather than taking it by const& and then later copying it. This is because you allow the caller the option to in turn std::move the shared_ptr when calling your function, thus saving yourself a set of increment and decrement operations. Or not. That is, the caller of the function can decide whether or not he needs the std::shared_ptr around after calling the function, and depending on whether or not move or not. This is not achievable if you pass by const&, and thus it is then preferably to take it by value.

如果您想将其复制到某处,则通过复制获取它并在内部使用 std::move 更有意义,而不是通过 const& 获取然后再复制它。这是因为您允许调用者在调用函数时轮流 std::move shared_ptr 的选项,从而为自己节省一组递增和递减操作。或不。也就是说,函数的调用者可以在调用函数后决定他是否需要std::shared_ptr,取决于是否移动。如果通过 const& 传递,这是无法实现的,因此最好按值获取它。

Of course, if the caller both needs his shared_ptr around for longer (thus can not std::move it) and you don't want to create a plain copy in the function (say you want a weak pointer, or you only sometimes want to copy it, depending on some condition), then a const& might still be preferable.

当然,如果调用者都需要他的 shared_ptr 更长时间(因此不能 std::move 它)并且您不想在函数中创建一个普通副本(假设您想要一个弱指针,或者您只是有时想要复制它,取决于某些条件),那么 const& 可能仍然更可取。

For example, you should do

例如,你应该做

void enqueue(std::shared<T> t) m_internal_queue.enqueue(std::move(t));

over

超过

void enqueue(std::shared<T> const& t) m_internal_queue.enqueue(t);

Because in this case you always create a copy internally

因为在这种情况下你总是在内部创建一个副本

回答by Hyunjik Bae

Not knowing time cost of shared_copy copy operation where atomic increment and decrement is in, I suffered from much higher CPU usage problem. I never expected atomic increment and decrement may take so much cost.

不知道原子增量和减量所在的 shared_copy 复制操作的时间成本,我遇到了更高的 CPU 使用率问题。我从没想过原子增量和减量可能会花费如此多的成本。

Following my test result, int32 atomic increment and decrement takes 2 or 40 times than non-atomic increment and decrement. I got it on 3GHz Core i7 with Windows 8.1. The former result comes out when no contention occurs, the latter when high possibility of contention occurs. I keep in mind that atomic operations are at last hardware based lock. Lock is lock. Bad to performance when contention occurs.

根据我的测试结果,int32 原子增量和减量比非原子增量和减量需要 2 或 40 倍。我在带有 Windows 8.1 的 3GHz Core i7 上得到了它。前者的结果是在没有发生争用的情况下出现的,后者是在发生争用的可能性大的时候出现的。我记住原子操作最终是基于硬件的锁。锁就是锁。发生争用时对性能不利。

Experiencing this, I always use byref(const shared_ptr&) than byval(shared_ptr).

遇到这种情况,我总是使用 byref(const shared_ptr&) 而不是 byval(shared_ptr)。

回答by Flamefire

There was a recent blog post: https://medium.com/@vgasparyan1995/pass-by-value-vs-pass-by-reference-to-const-c-f8944171e3ce

最近有一篇博文:https: //medium.com/@vgasparyan1995/pass-by-value-vs-pass-by-reference-to-const-c-f8944171e3ce

So the answer to this is: Do (almost) never pass by const shared_ptr<T>&.
Simply pass the underlying class instead.

所以这个问题的答案是:做(几乎)从不经过const shared_ptr<T>&
只需传递基础类即可。

Basically the only reasonable parameters types are:

基本上唯一合理的参数类型是:

  • shared_ptr<T>- Modify and take ownership
  • shared_ptr<const T>- Don't modify, take ownership
  • T&- Modify, no ownership
  • const T&- Don't modify, no ownership
  • T- Don't modify, no ownership, Cheap to copy
  • shared_ptr<T>- 修改并取得所有权
  • shared_ptr<const T>- 不要修改,拥有所有权
  • T&- 修改,无所有权
  • const T&- 不要修改,没有所有权
  • T- 不要修改,没有所有权,复制便宜

As @accel pointed out in https://stackoverflow.com/a/26197326/1930508the advice from Herb Sutter is:

正如@accel 在https://stackoverflow.com/a/26197326/1930508 中指出的,Herb Sutter 的建议是:

Use a const shared_ptr& as a parameter only if you're not sure whether or not you'll take a copy and share ownership

仅当您不确定是否要复制并共享所有权时,才使用 const shared_ptr& 作为参数

But in how many cases are you not sure? So this is a rare situation

但在多少情况下你不确定?所以这是一种罕见的情况

回答by artm

It's known issue that passing shared_ptr by value has a cost and should be avoided if possible.

众所周知,按值传递 shared_ptr 是有代价的,应该尽可能避免。

The cost of passing by shared_ptr

通过 shared_ptr 的代价

Most of the time passing shared_ptr by reference, and even better by const reference, would do.

大多数时候通过引用传递 shared_ptr ,甚至通过 const 引用传递更好,都可以。

The cpp core guideline has a specific rule for passing shared_ptr

cpp 核心指南有一个特定的规则来传递 shared_ptr

R.34: Take a shared_ptr parameter to express that a function is part owner

R.34:带一个shared_ptr参数来表示一个函数是part owner

void share(shared_ptr<widget>);            // share -- "will" retain refcount

An example of when passing shared_ptr by value is really necessary is when the caller passes a shared object to an asynchronous callee - ie the caller goes out of scope before the callee completes its job. The callee must "extend" the lifetime of the shared object by taking a share_ptr by value. In this case, passing a reference to shared_ptr won't do.

一个真正需要按值传递 shared_ptr 的例子是当调用者将共享对象传递给异步被调用者时 - 即调用者在被调用者完成其工作之前超出范围。被调用者必须通过按值获取 share_ptr 来“延长”共享对象的生命周期。在这种情况下,传递对 shared_ptr 的引用是行不通的。

The same goes for passing a shared object to a work thread.

将共享对象传递给工作线程也是如此。

回答by stonemetal

shared_ptr isn't large enough, nor do its constructor\destructor do enough work for there to be enough overhead from the copy to care about pass by reference vs pass by copy performance.

shared_ptr 不够大,它的构造函数\析构函数也没有做足够的工作来从副本中获得足够的开销来关心传递引用与传递副本的性能。