关于c ++:我们应该按引用还是按值传递shared_ptr?

Should we pass a shared_ptr by reference or by value?

当函数采用shared_ptr(来自boost或C ++ 11 STL)时,是否要传递它:

  • 通过const参考:void foo(const shared_ptr< T >& p)

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

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

请问您提供选择的理由,或者,如果是这种情况,为什么您认为这没有关系。


Scott,Andrei和Herb在C ++和Beyond 2011的"任何问题"会议上讨论并回答了这个问题。请从4:34观看shared_ptr性能和正确性。

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

除非您可以按照上面链接的视频中Scott Meyers的说明进行移动优化,但这与您可以使用的C ++的实际版本有关。

在GoingNative 2012会议的"互动小组:任何问题!"中对该讨论进行了重大更新。值得一看,尤其是从22:50开始。


这是赫伯·萨特的

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&).


我个人将使用const引用。无需为了函数调用而再次递减参考计数。


通过const引用,速度更快。如果您需要存储它,请在某个容器中添加参考。计数将通过复制操作自动增加。


我运行以下代码,一次是使用fooshared_ptr乘以const&,另一次是使用fooshared_ptr乘以值。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
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;
}

在我的Intel Core 2 Quad(2.4GHz)处理器上使用VS2015,x86版本构建

1
2
const shared_ptr&     - 10ms  
shared_ptr            - 281ms

按值复制版本要慢一个数量级。
如果要从当前线程同步调用函数,则最好使用const&版本。


从C ++ 11开始,您应该比您想像的更多地重视const&的价值。

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

如果您想将其复制到某个地方,则更明智的做法是先进行复制,然后在内部进行std :: move,而不是通过const&进行复制,然后再进行复制。这是因为在调用函数时,允许调用者选择std :: move shared_ptr的选项,从而为自己节省了一组递增和递减操作。或不。也就是说,函数的调用者可以在调用函数之后并根据是否移动来决定是否需要std :: shared_ptr。如果您通过const&,这是无法实现的,因此最好按值取值。

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

例如,您应该

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

过度

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

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


我不知道其中原子增量和减量所在的shared_copy复制操作的时间成本,我遭受了CPU使用率更高的问题。我从没想到原子的增加和减少会花费那么多的成本。

根据我的测试结果,int32原子增量和减量是非原子增量和减量的2或40倍。我在装有Windows 8.1的3GHz Core i7上获得它。前一个结果是在没有争用发生时出现的,后者是在发生争用的可能性很高时出现的。我要记住,原子操作最终是基于硬件的锁定。锁就是锁。发生争用时对性能不利。

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


shared_ptr不够大,它的构造函数/析构函数也做不到足够的工作,以使副本没有足够的开销来关心按引用传递与按副本传递的性能。