【问题标题】:Why return by reference?为什么按引用返回?
【发布时间】:2014-05-19 16:06:33
【问题描述】:

我正在阅读“SAMS 在 21 天内自学 C++”,遇到了一个我似乎无法理解的示例:

#include<iostream>

using namespace std;

class Counter
{
  public:
    Counter() {itsVal=0;}
    const Counter& operator++ ();
    int GetItsVal() {return itsVal;}
  private:
    int itsVal;
};

const Counter& Counter::operator++()
{
  ++itsVal;
  return *this;
}

int main()
{
  Counter i;
  Counter a = ++i;
  cout << "a: " << a.GetItsVal() << " i: " << i.GetItsVal() << endl;
  ++a;
  cout << "a: " << a.GetItsVal() << " i: " << i.GetItsVal() << endl;
}

为什么++运算符的声明中有一个“&”?我理解这意味着 ++ 运算符的返回类型是一个引用,但它似乎不是对 i 的引用(因为递增 a 不会递增 i)。我注意到如果我删除两个“&”,代码会返回相同的结果,但可能效率不高。

【问题讨论】:

  • 不能简单地在 21 天内教 C++。
  • @phresnel 这就像写 std::cout &lt;&lt; "Hello World" 并说“我现在知道 C++”。
  • " 但它似乎不是对 i 的引用。它引用 i。把它想象成i.Increment()。你在哪里增加a
  • @rockclimber112358:一般来说,很多专家认为那本书“坏”到“有害”;不仅是因为它的标题。

标签: c++ reference return return-type


【解决方案1】:

在 C++ 中,当您创建引用时,它是原始对象的代理:

int i = 0;
int& a = i;

++i;
std::cout << i << " " << a << "\n"; // prints '1 1'

++a;
std::cout << i << " " << a << "\n"; // prints '2 2'

现在,在你的情况下:

  • operator++ 返回对当前对象的引用
  • Counter a = ++i; 创建一个新对象(Counter 之后没有 &amp;)初始化为对 i 的引用的副本

如果您希望a 引用i,则需要更改其声明:

Counter& a = ++i;
       ^

这将无法编译思想,因为Counter::operator++ 的返回值是Counter const&amp;:您需要在此处删除const,这对于operator++ 来说是非惯用的,因此:

Counter& Counter::operator++() { ++itsVal; return *this; }

【讨论】:

    【解决方案2】:

    许多函数的返回值和一般行为都有常见的观察模式,让您可以轻松自然地使用它们。

    预递增和预递减运算符在执行链接任务后总是返回对对象的引用:

    Counter& Counter::operator++()
    Counter& Counter::operator--()
    

    虽然后增量和后减运算符要么不返回任何值,要么不返回包含旧值的临时值,以便轻松复制值类型:

    Counter Counter::operator++(int)
    Counter Counter::operator--(int)
    

    或者

    void Counter::operator++(int)
    void Counter::operator--(int)
    

    您的示例中似乎有人没有遵循惯例。

    【讨论】:

    • 我认为您对 post/pre 的描述有误,不是吗?
    • @jrok: 永远记不住要调用哪个 post 和哪个 pre。其他一切都应该是正确的,现在您添加了一些 -- 标志。
    • post-version 的签名中有一个int "postfix",我就是这么记的。
    • @Deduplicator 前缀增量运算符声明类似于简单的一元加运算符:X&amp; operator++();X operator+();。并且在代码中:a = ++b; vs a = +b;(运算符放在操作数之前,pre-)。后缀一个(实际上不是,但是...)类似于二进制加号运算符:X operator++(int);X operator+(int);(某些类的可能变体)。并且在代码中:a = b++; vs a = b + 1;(运算符放在 [first] 操作数之后,post-)。也许它可以帮助记住。
    【解决方案3】:

    为了让您更容易理解这一点,请考虑赋值运算符重载。


    Counter& 运算符 = (const Counter& RHS);

    计数器 a(10), b(20), c, d;

    现在,

    c = d = a 是可能的。当您返回某些内容作为参考时,该重载调用可以用作 R-Value。

    【讨论】:

    • 所以,如果我理解正确的话,返回引用将允许我使用返回的值来更改引用的值,对吧?但是,这不是这个例子中发生的事情,不是吗?
    【解决方案4】:

    考虑一下:

    void someFunction(const Counter& c);
    void someFunction(Counter& c);
    

    ...

    {
      Counter myCount = makeACounter();
      while(myCount.stillCounting()) {
        someFunction(++myCounter);
      }
    }
    

    someFunction() 需要一个引用,因此您最不想做的事情是复制 myCounter,因为这可能效率低下。此外,如果选择了 someFunction 的非 const 变体(在这种情况下),传递一个副本在逻辑上是错误的,因为 someFunction 可能会合法地修改其参数的状态,并且您希望在 myCounter 中看到该函数的更改返回。

    由于上述情况(和其他情况),operator++ 应该返回一个对*this 的非常量引用(你刚刚修改了 *this,所以它不能是 const 可以吗?)

    因此方法签名应该是: Counter&amp; operator++ ();

    【讨论】:

    • 简单:这里支持和鼓励 Markdown 语法,Stack Overflow 对代码区域进行着色。您可能想edit您的回答,以查看我的更改。
    猜你喜欢
    • 1970-01-01
    • 2021-11-19
    • 2016-09-28
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2022-11-16
    • 1970-01-01
    相关资源
    最近更新 更多