C ++ возвращает ссылочную переменную при перегрузке префикса - PullRequest
1 голос
/ 08 октября 2011

Я в замешательстве, когда весь код, который я нахожу, показывает возврат ссылочной переменной при перегрузке префиксного оператора. Я просмотрел часто задаваемые вопросы по parashift.com (http://www.parashift.com/c++-faq-lite/operator-overloading.html#faq-13.14), и он не ясен, хотя он говорит, что он будет понятен, когда вы его прочитаете. Я адаптировал их пример в бессмысленную, бессмысленную небольшую программу для тестирования.

#include<iostream>
using namespace std;

class Number {
   public:
      Number& operator++ ();    // prefix ++
      Number  operator++ (int); // postfix ++
      int value() { return value_; }
      void setValue(int value) { value_ = value; }
   private:
      int value_;
};

Number& Number::operator++ () {
   ++value_;
   return *this;
}

Number Number::operator++ (int unused) {
   Number temp;
   temp.setValue(value_);
   ++value_;
   return temp;
}

int main()
{
   Number someNum;
   someNum.setValue(20);
   cout << "someNum : " << someNum.value() << "\n";
   someNum++;
   ++someNum;
   cout << "someNum : " << someNum.value() << "\n";
   return 0;
}

Проблема в том, что это работает, если я просто объявляю его как объект Number так:

#include<iostream>
using namespace std;

class Number {
   public:
      Number operator++ ();    // prefix ++
      Number  operator++ (int); // postfix ++
      int value() { return value_; }
      void setValue(int value) { value_ = value; }
   private:
      int value_;
};

Number Number::operator++ () {
   ++value_;
   return *this;
}

Number Number::operator++ (int unused) {
   Number temp;
   temp.setValue(value_);
   ++value_;
   return temp;
}

int main()
{
   Number someNum;
   someNum.setValue(20);
   cout << "someNum : " << someNum.value() << "\n";
   someNum++;
   ++someNum;
   cout << "someNum : " << someNum.value() << "\n";
   return 0;
}

Полагаю, мне просто нужно лучше понять ссылочные переменные. Кто-нибудь может просто объяснить, почему префиксный оператор ДОЛЖЕН быть закодирован как возвращающий ссылочную переменную?

Ответы [ 2 ]

5 голосов
/ 08 октября 2011

Разница между

Number& Number::operator++ () {
   ++value_;
   return *this;
}

и

Number Number::operator++ () {
   ++value_;
   return *this;
}

заключается в том, что при использовании первого кода следующее выражение:

++(++(++someNum)); 

приращенияsomeNum Трижды.Смотрите вывод здесь: http://ideone.com/y9UlY

Однако, когда вы используете второй, это

++(++(++someNum));

увеличивает someNum только ОДИН РАЗ !!Смотрите вывод здесь: http://ideone.com/eOLdj

Это потому, что когда вы возвращаете ссылку из operator++(), второй и третий ++ вызывает один и тот же объект с именем someNum и, следовательно, он увеличивает то же самоеобъект, все время.Но когда вы возвращаете по значению, второй и третий ++ вызывает временный объект, который вы вернули из operator++().Следовательно, второй и третий вызов не увеличивают someNum, вместо этого он увеличивает временные объекты, которые уничтожаются в конце выражения.

Теперь, если временные объекты уничтожаются, зачем создавать затем впервое место?В конце концов, его оператор предварительного увеличения, который означает, что временный и исходный объект будут иметь одинаковое значение.Таким образом, хорошее дизайнерское решение - возвращаться по ссылке при определении оператора предварительного увеличения, чтобы избежать создания временных и повысить производительность.

2 голосов
/ 08 октября 2011

Во-первых, есть проблема эффективности.Вы создаете новый экземпляр класса, чтобы возвращать его без причины.

Во-вторых, существует семантическая проблема.Ваш код вызывает пустой конструктор или конструктор копирования для создания временного, а затем уничтожает временный.Если это имеет семантическое значение, которое неуместно, то код на самом деле не работает, он просто выглядит так.

В-третьих, код возвращает неправильную вещь.Рассмотрим: ++foo.do_something();.С вашим кодом мы вызываем do_something для временного объекта.Мы хотели позвонить do_something() на предварительно увеличенный foo.

...