C++ 我们应该通过引用还是通过值传递共享的\u ptr?

C++ 我们应该通过引用还是通过值传递共享的\u ptr?,c++,c++11,boost,shared-ptr,C++,C++11,Boost,Shared Ptr,当函数接受一个共享\u ptr(来自boost或C++11 STL)时,您是否传递它: 按常量参考:void foo(常量共享\u ptr&p) 或按值:void foo(共享) 我更喜欢第一种方法,因为我怀疑它会更快。但这真的值得吗?或者还有其他问题吗 请给出您选择的原因,或者如果是这种情况,为什么您认为这无关紧要。我个人会使用const参考。无需为了函数调用而增加引用计数以再次减少它。通过constreference,速度更快。如果您需要将其存储在某个容器中,则复制操作会自动神奇地增加引

当函数接受一个
共享\u ptr
(来自boost或C++11 STL)时,您是否传递它:

  • 按常量参考:
    void foo(常量共享\u ptr&p)

  • 或按值:
    void foo(共享)

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


请给出您选择的原因,或者如果是这种情况,为什么您认为这无关紧要。

我个人会使用
const
参考。无需为了函数调用而增加引用计数以再次减少它。

通过
const
reference,速度更快。如果您需要将其存储在某个容器中,则复制操作会自动神奇地增加引用计数。

共享\u ptr不够大,它的构造函数\析构函数也没有做足够的工作,以使副本产生足够的开销,从而关心通过引用传递与通过副本传递的性能。

Scott、Andrei和Herb在的会话中讨论并回答了这个问题。从4点34分开始看

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

除非你能在上面的谈话视频中解释Scott Meyers的优化,但是这与C++的实际版本有关,你可以使用.< 这次讨论的一个重要更新发生在会议期间,值得一看,特别是从

指导原则:不要将智能指针作为函数参数传递,除非 您希望使用或操作智能指针本身,例如 分享或转让所有权

准则:表示函数将存储和共享 使用by value shared_ptr参数的堆对象

准则:使用 non-const shared_ptr&仅用于修改共享_ptr的参数。使用 const shared_ptr&仅当您不确定是否 不,你会复制并分享所有权;否则,请使用小部件* 取而代之的是(如果不可为null,则为小部件&)


我运行下面的代码,一次使用
foo
通过
const&
获取
shared\u ptr
,另一次使用
foo
通过值获取
shared\u 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;
}
按值复制版本的速度慢了一个数量级。

如果您从当前线程同步调用函数,请选择
const&
版本。

不知道原子增量和减量所在的共享拷贝操作的时间成本,我遇到了更高的CPU使用率问题。我从来没想到原子的增量和减量会花费这么多的成本

根据我的测试结果,int32原子增量和减量比非原子增量和减量要花费2到40倍。我在Windows8.1的3GHz CoreI7上得到了它。前者在没有发生争用时产生,后者在发生争用的可能性很高时产生。我要记住,原子操作最终是基于硬件的锁。锁就是锁。发生争用时会对性能造成不良影响


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

因为C++11,你应该比你想象的更频繁地使用它

如果您使用std::shared_ptr(而不是底层类型T),那么您这样做是因为您想用它做点什么

如果您想在某个地方复制它,那么通过复制和std::在内部移动它会更有意义,而不是通过const&然后再复制它。这是因为您允许调用者在调用函数时依次选择std::move shared_ptr,从而节省了一组递增和递减操作。或者不是。也就是说,函数的调用方可以在调用函数后决定是否需要std::shared_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;
}
当然,如果调用方都需要其共享的ptr存在更长的时间(因此无法std::move它),并且您不想在函数中创建普通副本(比如您想要弱指针,或者您只是有时想要复制它,这取决于某些条件),那么const&可能仍然是更好的选择

例如,你应该这样做

void enqueue(std::shared<T> t) m_internal_queue.enqueue(std::move(t));
void排队(std::shared t)m_internal_queue.enqueue(std::move(t));
结束

void排队(std::shared const&t)m_internal_queue.enqueue(t);

因为在这种情况下,您总是在内部创建副本

最近有一篇博文:

所以答案是:不要(几乎)路过
constshared\u ptr&

只需传递基础类即可

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

  • 共享\u ptr
    -修改并获得所有权
  • 共享\u ptr
    -不修改,拥有所有权
  • T&
    -修改,无所有权
  • const T&
    -不修改,没有所有权
  • T
    -不修改,没有所有权,复制成本低
正如@accel在Herb Sutter的建议中指出的那样:

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


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

众所周知,按值传递共享的ptr是有成本的,如果可能的话应该避免

大多数情况下,通过引用传递共享_ptr,甚至通过const引用传递更好

cpp核心指南对通过共享ptr有一个特定规则

无效共享(shar)
void enqueue(std::shared<T> const& t) m_internal_queue.enqueue(t);
void share(shared_ptr<widget>);            // share -- "will" retain refcount