【问题标题】:Is returning a reference ever a good idea?返回参考是一个好主意吗?
【发布时间】:2012-10-16 04:35:01
【问题描述】:

我们都知道returning a reference to a local variable is a bad idea。但是,我想知道返回引用是否真的是一个好主意,是否有可能确定一些关于何时或何时不这样做的好规则。

我返回引用的问题是调用函数需要关心对象的生命周期,这不应该是它的责任。作为一个人为的例子:

#include <vector>

const int& foo() {
  std::vector<int> v = {1, 2, 3, 4, 5};
  return v[0];
}

int main(int argc, const char* argv[])
{
  const int& not_valid = foo();
  return 0;
}

这里,vectorfoo 末尾超出范围,破坏其内容并使对其元素的任何引用无效。 vector::operator[] 返回对元素的引用,因此当进一步从 foo 返回此引用时,main 中的引用悬空。我不相信 const 引用会延长这里的生命周期,因为它不是对临时的引用。

正如我所说,这是一个人为的例子,foo 的作者可能不会那么愚蠢地尝试返回v[0] 作为参考。但是,很容易看出返回引用如何要求调用者关心它不拥有的对象的生命周期。将元素推入 vector 会复制它,因此 vector 负责它。传递引用参数不存在此问题,因为您知道函数将在调用者继续并销毁对象之前完成。

我可以看到返回引用允许使用类似数组的语法,例如 v[0] = 5 - 但是拥有像 v.set(index, value) 这样的成员函数有什么不好呢?至少这样我们就不会暴露内部对象。我知道返回引用也可能会提高性能,但是对于 RVO、命名 RVO (NRVO) 和移动语义,它可以忽略不计或不存在。

所以我一直试图想象在哪些情况下返回引用是真正安全的,但我无法理解它可能涉及的所有不同的所有权语义排列。关于何时执行此操作有什么好的规则吗?

注意:我知道在vectors 中处理所有权的更好方法是使用智能指针,但是使用不同的对象会遇到同样的问题 - 谁拥有智能指针?

【问题讨论】:

  • 使用智能指针的不同对象不会有同样的问题。智能指针始终按值存储,存储值的人拥有指针。我没有看到那里的问题。另外,我不知道您所说的返回“真正安全”的参考是什么意思。你能说明为什么 operator[] 返回引用不如使用 set 函数“安全”吗?
  • 为什么只是引用?相同的参数对指针有效。
  • @sftrabbit 除非您存储了对智能指针的引用,否则这是一个有争议的问题,因为您总是按值存储智能指针,否则它们毫无价值。
  • 只考虑“本地”传递。 v 是本地的,v 包含的任何值也是如此。
  • @sftrabbit 哦,我明白了。在这种情况下,RVO、NRVO 和移动语义绝对不会帮助您,因为您没有返回要从中移动的过期值,或者使用可以通过 RVO 就地构建的临时值。无法复制对象(因为您最终得到 两个,而不是一个),这可能会很昂贵。

标签: c++ reference ownership


【解决方案1】:

返回引用有很多很好的用途。如您所说,一种是模拟本机取消引用运算符之类的东西:

struct Goo
{
    int & operator[](size_t i) { return arr[i]; }
    int & front()              { return arr[0]; }

    // etc.

private:
    int * arr;
};

另一个用例是当您返回对传入事物的引用时。典型示例是像 &lt;&lt; 这样的可链接操作:

std::ostream & operator<<(std::ostream & os, Goo const & g)
{ 
    return os << g[3];
}

作为最后一个例子,这是一个线程安全的全局对象:

Goo & get_the_object()
{
    static Goo impl;
    return impl;
}

引用是语言的一个组成部分,它们很可能由函数调用返回。正如您所说,了解对象的生命周期很重要,但这始终是正确的,而不是返回引用的特定问题。

【讨论】:

  • +1,但要小心最后一个例子,静态销毁顺序和所有爵士乐。
【解决方案2】:

就我个人而言,当我想实现单例模式时,我喜欢返回对静态变量的引用

SomeClass& getTheSingleton()
{
    static SomeClass theInstance;
    return theInstance;
}

我不必编写任何涉及是否初始化某个指针的逻辑,它使我可以控制静态初始化的顺序

【讨论】:

    猜你喜欢
    • 2020-09-29
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2010-11-05
    • 1970-01-01
    • 2014-10-15
    相关资源
    最近更新 更多