Answers:
按值返回智能指针。
如您所说,如果您通过引用将其返回,将不会适当地增加引用计数,从而增加了在不适当的时间删除某些内容的风险。仅凭这一点就足以成为不作为参考返回的理由。接口应该健壮。
如今,由于返回值优化(RVO),成本问题变得毫无意义,因此您不会产生增量-增量-减量序列或现代编译器中的类似过程。因此,返回a的最佳方法shared_ptr
是简单地按值返回:
shared_ptr<T> Foo()
{
return shared_ptr<T>(/* acquire something */);
};
对于现代C ++编译器来说,这是一个非常明显的RVO机会。我知道,即使关闭所有优化,Visual C ++编译器仍会实现RVO。而使用C ++ 11的移动语义,这种关注就不再那么重要了。(但唯一可以确定的方法是剖析和试验。)
如果您仍然不确定,Dave Abrahams 的文章将论证按值返回。我在这里复制一个片段;我强烈建议您阅读全文:
老实说:以下代码会让您感觉如何?
std::vector<std::string> get_names(); ... std::vector<std::string> const names = get_names();
坦白说,即使我应该更了解,也让我感到紧张。原则上,当
get_names()
返回时,我们要复制vector
的string
秒。然后,我们需要在初始化时再次复制它names
,并且需要销毁第一个副本。如果string
向量中有N s,则每个副本可能需要多达N + 1个内存分配,并且在复制字符串内容时,整个缓存不友好的数据访问都需要>。与其面对这种焦虑,不如我经常放弃引用传递以避免不必要的复制:
get_names(std::vector<std::string>& out_param ); ... std::vector<std::string> names; get_names( names );
不幸的是,这种方法远非理想。
- 代码增长了150%
- 我们必须放弃
const
-ness,因为我们正在对名称进行突变。- 正如函数程序员喜欢提醒我们的那样,通过破坏引用透明性和方程式推理,变异使代码的推理更加复杂。
- 我们不再对名称使用严格的值语义。
但是,是否真的有必要以这种方式弄乱我们的代码以提高效率?幸运的是,答案是“否”(尤其是如果您使用的是C ++ 0x)。
cout << "Hello World!";
在默认复制构造器中有一条语句,那么Hello World!
当RVO生效时,您将看不到两个。但是,对于正确设计的智能指针,即使是wrt同步,这也不是问题。
关于任何智能指针(不仅是shared_ptr),我都不认为返回对一个指针的引用是可以接受的,而且我非常不愿意通过引用或原始指针来传递它们。为什么?因为您不能确定以后不会通过引用将其浅复制。您的第一点定义了应引起关注的原因。即使在单线程环境中也可能发生这种情况。您不需要并发访问数据即可在程序中放置错误的副本语义。一旦将指针传递出去,您就无法真正控制用户的操作,因此,请勿鼓励滥用为API用户提供足够的绳索以使其自身吊起。
其次,如果可能的话,查看您的智能指针的实现。建造和破坏应几乎可以忽略不计。如果这种开销是不可接受的,那么不要使用智能指针!但是除此之外,您还需要检查现有的并发体系结构,因为对跟踪指针使用情况的机制的互斥访问不仅会使shared_ptr对象的构造慢下来,而且会减慢您的工作速度。
编辑,三年后:随着C ++中更现代的功能的出现,我将调整答案,以便更多地接受这样的情况:您只编写了一个lambda,而该lambda永远不会超出调用函数的范围,并且不会复制到其他地方。在这里,如果您希望节省复制共享指针的非常少的开销,那将是公平且安全的。为什么?因为您可以保证引用不会被滥用。