1

我们都知道返回对局部变量的引用是个坏主意。但是,我想知道返回引用是否真的是一个好主意,以及是否有可能确定一些关于何时或何时不这样做的好规则。

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

#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;
}

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

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

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

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

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

4

2 回答 2

10

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

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

    // etc.

private:
    int * arr;
};

另一个用例是当您返回对传入事物的引用时。典型示例是可链接操作,例如<<

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

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

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

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

于 2012-10-25T22:09:28.093 回答
0

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

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

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

于 2012-10-26T00:15:30.063 回答