假设我有一个带有返回 shared_ptr
的方法的类。
按引用或按值返回它可能有哪些好处和缺点?
两个可能的线索:
早期对象破坏。如果我通过(const)引用返回shared_ptr,则引用计数器不会增加,因此当对象超出另一个上下文(例如另一个线程)的范围时,我会承担删除对象的风险。这个对吗?如果环境是单线程的,会不会也出现这种情况呢?
成本。价值传递当然不是免费的。是否值得尽可能避免它?
谢谢大家。
按值返回智能指针。
正如您所说,如果您通过引用返回它,您将无法正确增加引用计数,这会带来在不适当的时间删除某些内容的风险。仅此一项就足以成为不通过引用返回的理由。接口应该是健壮的。
多亏了 return value optimization (RVO),成本问题现在已经没有实际意义,因此您不会在现代编译器中产生增量-增量-减量序列或类似的东西。所以返回 shared_ptr
的最佳方法是简单地按值返回:
shared_ptr<T> Foo()
{
return shared_ptr<T>(/* acquire something */);
};
对于现代 C++ 编译器来说,这是一个非常明显的 RVO 机会。我知道即使关闭所有优化,Visual C++ 编译器也会实现 RVO。而使用 C++11 的移动语义,这种担忧就更不重要了。 (但唯一可以确定的方法是分析和实验。)
如果您仍然不相信,Dave Abrahams 的 an article 提出了按值返回的论据。我在这里复制了一个片段;我强烈建议您阅读整篇文章:
老实说:下面的代码让你感觉如何? std::vector
关于任何智能指针(不仅仅是 shared_ptr),我认为返回一个引用是不可接受的,而且我会非常犹豫是否通过引用或原始指针传递它们。为什么?因为你不能确定它以后不会通过引用被浅拷贝。您的第一点定义了为什么这应该是一个问题的原因。即使在单线程环境中也可能发生这种情况。您不需要并发访问数据即可将错误的复制语义放入您的程序中。一旦将指针传递出去,您就无法真正控制用户对指针的操作,因此不要鼓励滥用,给您的 API 用户足够的绳索让自己上吊。
其次,如果可能的话,看看你的智能指针的实现。建造和破坏应该几乎可以忽略不计。如果这个开销是不可接受的,那么不要使用智能指针!但除此之外,您还需要检查您所拥有的并发架构,因为对跟踪指针使用的机制的互斥访问会比仅仅构建 shared_ptr 对象更慢。
编辑,3 年后:随着 C++ 中更现代的特性的出现,我会调整我的答案以更容易接受当你简单地编写一个永远不会存在于调用函数范围之外的 lambda 并且不是复制到别的地方。在这里,如果您想节省复制共享指针的最小开销,那将是公平和安全的。为什么?因为你可以保证引用永远不会被误用。
不定期副业成功案例分享
cout << "Hello World!";
语句,则当 RVO 生效时,您不会看到两个Hello World!
。然而,这对于正确设计的智能指针来说应该不是问题,即使是同步也是如此。shared_ptr
是参数,按值传递或 const 引用怎么办?