Vin*_*Pii 86 c++ return smart-pointers
假设我有一个带有返回a的方法的类shared_ptr.
通过引用或值返回它可能带来的好处和缺点是什么?
两个可能的线索:
shared_ptrby(const)引用,则引用计数器不会递增,因此当它在另一个上下文(例如另一个线程)中超出范围时,我会冒被删除的风险.它是否正确?如果环境是单线程的,那么这种情况也会发生吗?谢谢大家.
In *_*ico 104
按值返回智能指针.
正如您所说,如果您通过引用返回它,您将无法正确增加引用计数,这可能会在不适当的时间删除某些内容.仅这一点就足以成为不能通过引用返回的理由.接口应该是健壮的.
由于返回值优化(RVO),成本问题现在没有实际意义,所以你不会在现代编译器中产生递增 - 递减 - 递减序列或类似的东西.因此,返回a的最佳方法shared_ptr是简单地按值返回:
shared_ptr<T> Foo()
{
return shared_ptr<T>(/* acquire something */);
};
Run Code Online (Sandbox Code Playgroud)
对于现代C++编译器来说,这是一个显而易见的RVO机会.我知道即使关闭所有优化,Visual C++编译器也会实现RVO.而对于C++ 11的移动语义,这种担忧甚至更不重要.(但唯一可以确定的方法是剖析和实验.)
如果你仍然不相信,戴夫亚伯拉罕有一篇文章,为价值回归做出争论.我在这里复制一个片段; 我强烈建议您阅读整篇文章:
说实话:以下代码如何让您感觉到?
Run Code Online (Sandbox Code Playgroud)std::vector<std::string> get_names(); ... std::vector<std::string> const names = get_names();坦率地说,尽管我应该知道更好,但这让我感到紧张.原则上,当
get_names()返回时,我们要复制vector的string秒.然后,我们需要在初始化时再次复制它names,我们需要销毁第一个副本.如果string向量中有N s,则每个副本可能需要多达N + 1个内存分配和一系列对缓存不友好的数据访问>复制字符串内容.我没有面对那种焦虑,而是经常躲过参考,以避免不必要的副本:
Run Code Online (Sandbox Code Playgroud)get_names(std::vector<std::string>& out_param ); ... std::vector<std::string> names; get_names( names );不幸的是,这种方法远非理想.
- 代码增长了150%
- 我们不得不放弃,
const因为我们正在改变名字.- 正如功能程序员想要提醒我们的那样,突变通过破坏参照透明度和等式推理使得代码更加复杂.
- 我们不再对名称有严格的值语义.
但是,是否真的有必要以这种方式搞砸我们的代码来提高效率?幸运的是,答案结果是否定的(特别是如果你使用的是C++ 0x).
San*_*nto 19
关于任何智能指针(不仅仅是shared_ptr),我不认为返回一个引用是可以接受的,我会非常犹豫通过引用或原始指针传递它们.为什么?因为您无法确定以后不会通过引用进行浅层复制.您的第一点定义了为什么这应该是一个问题的原因.即使在单线程环境中也会发生这种情况.您不需要并发访问数据来在程序中放置错误的复制语义.一旦你将其传递掉,你就无法真正控制用户对指针的操作,所以不要鼓励误操作,让你的API用户有足够的绳索自行挂起.
其次,如果可能,请查看智能指针的实现.建筑和破坏应该接近于可以忽略不计.如果这个开销是不可接受的,那么不要使用智能指针!但除此之外,您还需要检查您已经获得的并发体系结构,因为对跟踪指针使用的机制的互斥访问将比仅仅构建shared_ptr对象减慢速度.
编辑,3年后:随着C++中更现代的功能的出现,我会调整我的答案,当你只是编写一个从不存在于调用函数范围之外的lambda时,更多地接受这种情况,而不是复制到其他地方.在这里,如果你想节省复制共享指针的最小开销,那将是公平和安全的.为什么?因为您可以保证参考永远不会被误用.