我应该通过引用传递shared_ptr吗?

时间:2011-12-05 12:40:57

标签: c++ shared-ptr

传递shared_ptr的最佳做法是什么?

目前我传递的shared_ptr函数参数如下:

void function1( shared_ptr<TYPE>& value );

3 个答案:

答案 0 :(得分:95)

在受控环境中,您可以通过常量引用传递共享指针。确保没有人同时删除该对象,但如果您对提供引用的人非常小心,这不应该太难。

通常,您应该将共享指针作为直接副本传递。这给了它预期的语义:每个包含共享指针副本的作用域凭借其在所有权中的“共享”来保持对象的活动。

不总是通过值传递的唯一原因是复制共享指针由于原子引用计数更新而以一定的价格出现;但是,这可能不是一个主要问题。


可选的题外话:

由于主要问题已得到解答,或许考虑一些永远使用共享指针的方法是有益的。这是一个小小的思想实验。让我们定义共享指针类型SF = std::shared_ptr<Foo>。为了考虑引用,让我们看一下类型RSF = std::reference_wrapper<T>而不是传递函数参数。也就是说,如果我们有一个共享指针SF p(std::make_shared<Foo>());,那么我们可以通过RSF w = std::ref(p);创建一个带有值语义的引用包装器。非常适合设置。

现在,每个人都知道指针的容器是雷区。所以std::vector<Foo*>将成为维护的噩梦,并且由于不正确的终身管理而产生任何数量的错误。更糟糕的是,从来没有明确谁拥有拥有容器存储指针的对象。指针甚至可以是指向动态对象,自动对象和垃圾的指针。没有人能说出来。因此标准解决方案是使用std::vector<SF>代替。这是使用共享指针的正确方法。另一方面,你永远不能使用的是std::vector<RSF> - 这是一个无法控制的怪物,它实际上与裸指针的原始向量非常相似!例如,不清楚您持有引用的对象是否仍然存在。参考共享指针已经打败了它的整个目的。

对于第二个例子,假设我们像以前一样有一个共享指针SF p。现在我们有一个我们想要并发运行的函数int foo(SF)。通常的std::thread(foo, p)工作得很好,因为线程构造函数会对其参数进行复制。但是,如果我们说std::thread(foo, std::ref(p)),我们会遇到各种各样的麻烦:调用范围中的共享指针可能会过期并销毁该对象,并且您将留下悬空引用和无效指针! / p>

我希望当你真正希望你的共享指针被 copy 传递时,这两个公认的相当人为的例子会让你有所了解。在一个设计良好的程序中,应该始终清楚谁负责哪些资源,如果使用得当,共享指针是一个很好的工具。

答案 1 :(得分:24)

这取决于你想要什么。被叫方应该共享对象的所有权吗?然后它需要自己的shared_ptr副本。所以按值传递它。

如果函数只需要访问调用者拥有的对象,请继续并传递(const)引用,以避免复制shared_ptr的开销。

C ++中的最佳实践是始终,以便为对象明确定义所有权语义。没有通用的“总是做到这一点”来取代实际的思想

如果始终按值传递共享指针,则代价很高(因为复制它们比原始指针要昂贵得多)。如果您从不这样做,那么首先使用共享指针毫无意义。

当新函数或对象需要共享指针对象的所有权时,复制共享指针。

答案 2 :(得分:8)

如果通过引用传递,则通过const引用传递它。这表明你出于性能原因通过了ref。此外,尽可能使用make_shared,因为它可以节省间接,因此可以提供性能提升。