如何通过引用或值返回智能指针(shared_ptr)? [英] How to return smart pointers (shared_ptr), by reference or by value?

查看:27
本文介绍了如何通过引用或值返回智能指针(shared_ptr)?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

假设我有一个类,其方法返回 shared_ptr.

Let's say I have a class with a method that returns a shared_ptr.

按引用或按值返回它可能有哪些好处和坏处?

What are the possible benefits and drawbacks of returning it by reference or by value?

两个可能的线索:

  • 早期对象销毁.如果我通过 (const) 引用返回 shared_ptr,则引用计数器不会增加,因此我会承担在以下情况下删除对象的风险它在另一个上下文(例如另一个线程)中超出范围.这个对吗?如果环境是单线程的,是否也会出现这种情况?
  • 成本.价值传递当然不是免费的.是否值得尽可能避免?
  • Early object destruction. If I return the shared_ptr by (const) reference, the reference counter is not incremented, so I incur the risk of having the object deleted when it goes out of scope in another context (e.g. another thread). Is this correct? What if the environment is single-threaded, can this situation happen as well?
  • Cost. Pass-by-value is certainly not free. Is it worth avoiding it whenever possible?

谢谢大家.

推荐答案

按值返回智能指针.

正如您所说,如果您通过引用返回它,您将无法正确增加引用计数,这会带来在不适当的时间删除某些内容的风险.仅此一项就足以成为不通过引用返回的理由.接口应该是健壮的.

As you've said, if you return it by reference, you won't properly increment the reference count, which opens up the risk of deleting something at the improper time. That alone should be enough reason to not return by reference. Interfaces should be robust.

由于 返回值优化 (RVO),成本问题现在已经没有实际意义了,所以你赢了在现代编译器中不会产生增量 - 增量 - 减量序列或类似的东西.所以返回 shared_ptr 的最佳方式是简单地按值返回:

The cost concern is nowadays moot thanks to return value optimization (RVO), so you won't incur a increment-increment-decrement sequence or something like that in modern compilers. So the best way to return a shared_ptr is to simply return by value:

shared_ptr<T> Foo()
{
    return shared_ptr<T>(/* acquire something */);
};

对于现代 C++ 编译器来说,这是一个非常明显的 RVO 机会.我知道即使关闭所有优化,Visual C++ 编译器也会实现 RVO.而使用 C++11 的移动语义,这种担忧就更不重要了.(但唯一可以确定的方法是分析和实验.)

This is a dead-obvious RVO opportunity for modern C++ compilers. I know for a fact that Visual C++ compilers implement RVO even when all optimizations are turned off. And with C++11's move semantics, this concern is even less relevant. (But the only way to be sure is to profile and experiment.)

如果您仍然不相信,Dave Abrahams 有 一篇文章,它提出了按值返回的论点.我在这里复制了一个片段;我强烈建议您阅读整篇文章:

If you're still not convinced, Dave Abrahams has an article that makes an argument for returning by value. I reproduce a snippet here; I highly recommend that you go read the entire article:

说实话:下面的代码让你感觉如何?

Be honest: how does the following code make you feel?

std::vector<std::string> get_names();
...
std::vector<std::string> const names = get_names();

坦率地说,即使我应该知道的更多,这让我感到紧张.原则上,当 get_names()返回,我们必须复制 stringvector.然后,我们需要在初始化的时候再复制一次names,我们需要销毁第一个副本.如果向量中有N个string,每个副本可能需要多达 N+1 个内存分配和大量对缓存不友好的数据访问 > 因为字符串内容被复制.

Frankly, even though I should know better, it makes me nervous. In principle, when get_names() returns, we have to copy a vector of strings. Then, we need to copy it again when we initialize names, and we need to destroy the first copy. If there are N strings in the vector, each copy could require as many as N+1 memory allocations and a whole slew of cache-unfriendly data accesses > as the string contents are copied.

我没有面对这种焦虑,而是经常使用传递引用来避免不必要的副本:

Rather than confront that sort of anxiety, I’ve often fallen back on pass-by-reference to avoid needless copies:

get_names(std::vector<std::string>& out_param );
...
std::vector<std::string> names;
get_names( names );

不幸的是,这种方法远非理想.

Unfortunately, this approach is far from ideal.

  • 代码增长了 150%
  • 我们不得不放弃 const-ness,因为我们正在改变名称.
  • 正如函数式程序员喜欢提醒我们的那样,变异通过破坏引用透明度和等式推理,使代码的推理变得更加复杂.
  • 我们不再有严格的名称值语义.
  • The code grew by 150%
  • We’ve had to drop const-ness because we’re mutating names.
  • As functional programmers like to remind us, mutation makes code more complex to reason about by undermining referential transparency and equational reasoning.
  • We no longer have strict value semantics for names.

但是为了提高效率真的有必要这样搞乱我们的代码吗?幸运的是,答案是否定的(尤其是如果您使用的是 C++0x).

这篇关于如何通过引用或值返回智能指针(shared_ptr)?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆