返回引用的意义是什么?

16

在C++中,

function() = 10;

如果该函数通过引用返回变量,则此方法有效。

它的使用情况是什么?


3
不过,那种语法不是必须的。你可以返回一个代理类对象,它的operator=在后台执行赋值操作。 - Johannes Schaub - litb
1
这就是为什么我从来没有完全理解C++的原因...你永远都会发现一些新的语法片段,完全让你大吃一惊...不过还是得喜欢运算符重载 :| - Luca Matteis
你甚至可以通过值返回并赋给一个临时变量。这就是为什么Scott Meyers建议通过const值返回。 - Philipp
只有当该函数是成员函数时,这才有意义。 - smerlin
11个回答

20

最常见的情况是实现像 operator[] 这样的东西。

struct A {
    int data[10];
    int & operator[]( int i ) {
         return data[i];
    }
};

另一种方法是通过访问器函数从类返回一个大对象:
struct b {
    SomeBigThing big;
    const SomeBigThing & MyBig() const {
         return big;
    }
};

为了避免复制开销。

7
考虑以下代码,MyFunction返回一个指向int的指针,并且您为该int设置了一个值。
int  *i;
i = MyFunction();
*i = 10;

现在将其缩短为:
*(MyFunction()) = 10;

它与第一个代码块完全相同。

你可以把引用看作是一个始终被解除引用的指针。因此,如果我的函数返回一个整数的引用而不是指针,则第一个代码块将变为

int  &i;
i = MyFunction();
i = 10;

第二个则变成

MyFunction() = 10;

这正是我在寻找的内容。

3
您所发布的代码不是合法的C++代码 - 引用必须进行初始化。 - anon

4

实例的获取器/设置器

class C
{
    int some_param_;
public:
    int& param() { return some_param_; }
    int const& param() const { return some_param_; }
};

但是在这里,你应该使用一些公共整数作为参数。容器提供了返回引用的函数,例如vector<T>::operator[],所以你可以写成v[k] = x


而且当你需要使用另一种类型的对象来替换some_param_时,这个实现很难进行重构。 - Basilevs

3

一个非常常见的用例是当你编写一个类似数组的类时。在这种情况下,您希望重载 operator[] 以便您可以执行 a[0] = 10; 在这种情况下,您需要将签名设置为 int& operator[](int index);


2

如果您有一个包含其他结构的类,直接修改包含的结构可能会很有用:

struct S
{
    int value;
};

class C
{
    public:

        S& ref() { return m_s; }

    private:

        S m_s;
};

允许您编写类似于:

void foo()
{
    C c;

    // Now you can do that:

    c.ref().value = 1;
}

注意:在这个例子中,直接将m_s公开可能比返回引用更加简单明了。


2

SO搞砸了我的答案

你甚至不需要返回一个引用:

struct C { };

C f() {
  return C();
}

int main() {
  C a;
  f() = a;  // compiles fine
}

由于这种行为相当令人惊讶,除非用户有合理的意图修改结果,否则通常应返回const值或const引用。


2

在实现访问器时,这将非常有用。

class Matrix
{
   public:
      //I skip constructor, destructor etc

      int & operator ()(int row, int col)
      {
         return m_arr[row + col * size];
      }

   private:
      int size;
      int * m_arr;
}

Matrix m(10);
m(1,0) = 10;  //assign a value to row 1, col 0

我不知道为什么,但我讨厌矩阵类(而我是一个数值分析师)。 - Alexandre C.

1

另一个经典案例:

class Foo {
  Foo();
public:
  static Foo& getSingleton();
};

0

如果你愿意,你也可以使用引用返回来实现方法链。

class A
{
public:
    A& method1()
    {
        //do something
        return *this;   //return ref to the current object
    }
    A& method2(int i);
    A& method3(float f);  //other bodies omitted for brevity
};

int main()
{
    A aObj;
    aObj.method1().method2(5).method3(0.75);

    //or use it like this, if you prefer
    aObj.method1()
        .method2(5)
        .method3(0.75);
}

0

命名参数惯用法是另一个使用案例。请考虑

class Foo
{
public:
    Foo(
        int lions,
        float tigers,
        double bears,
        std::string zookeeper
    );
};

这个类的用户需要记住每个参数的位置

Foo foo( 1, 2.0, 5, "Fred" );

如果不查看头文件,则可能非常不明显。与创建者类相比,如此

class CreateFoo
{
friend class Foo;
public:
    CreateFoo();

    CreateFoo& lions(int lions) {
        _lions = lions;
         return *this;
    }

    CreateFoo& tigers(float tigers) {
        _tigers = tigers;
        return *this;
    }

    CreateFoo& bears(double bears) {
        _bears = bears;
        return *this;
    }

    CreateFoo& zookeeper(const std::string& zookeeper) {
        _zookeeper = zookeeper;
        return *this;
    }

private:
    int _lions;
    float _tigers;
    double _bears;
    std::string _zookeeper;
};

这样客户端就可以使用它了。

Foo foo = CreateFoo().
    lions(1).
    tigers(2.0).
    zookeeper("Fred").
    bears(5)
    ;

假设 Foo 有一个构造函数,它需要一个 const CreateFoo&

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接