Страницы

Поиск по вопросам

пятница, 19 октября 2018 г.

Перегрузка операторов

Как определить возвращаемое значение при перегрузке операторов ? В каком случае следует вернуть ссылку на объект, а в каком его побитовую копию ?
update:
#include
using namespace std;
class SomeClass { int value; public: SomeClass(int value) { this->value = value; }
SomeClass& operator=(const SomeClass &rhs) { value = rhs.value;
return *this; }
SomeClass operator+(const SomeClass &rhs) { value += rhs.value;
return *this; } };
int main(int argc, char *argv[]) { SomeClass a(1), b(2);
a = a + b;
return 0; }


Ответ

Операторы бывают двух типов: операторы c побочными эффектами, которые модифицируют один из своих операндов (инкремент/декремент, простое присваивание, составные присваивания типа += и т.п.), и операторы без побочных эффектов, которые не модифицируют своих операндов, а возвращают результат в новом объекте.
Вот тут-то обычно и проходит "водораздел" по типу возвращаемого значения.
Первые традиционно возвращают ссылку на свой операнд, а вторые - самостоятельный временный объект. (О какой "побитовой копии" вы говорите - не ясно. Резльтат оператора обычно содержит новое значение, поэтому не ясно, как он может быть "копией" чего-то.)
В вашем примере оператор присвивания реализован как раз правильно, а вот бинарный оператор сложения реализован до ужаса криво.
Ваш бинарный оператор сложения модифицирует свой левый операнд. Где это видано? Это странное и неожиданное поведение для бинарного оператора сложения.
Правильная реализация могла бы выглядеть так
SomeClass operator +(const SomeClass &rhs) const { int new_value = value + rhs.value; return new_value; // <- Используется неявная конверсия `int` к `SomeClass` }
Оба операнда сохраняют свои исходные значения, и возврат результата, разумеется, делается по значению.
А вот если бы вы хотели перегрузить оператор составного присваивания +=, то это бы выглядело так
SomeClass &operator +=(const SomeClass &rhs) { value += rhs.value; return *this; }
т.е. именно так, как вы написали, но уже с возвратом по ссылке.
Отдельно стоит добавить, что, во-первых, такие "симметричные" по отношению к своим аргументам операторы, как бинарные +, -, * и т.д. рекомендуется перегружать самостоятельной функцией, а не функцией-членом (например, friend-функцией). Во-вторых, если вы перегружаете и бинарные операторы, и соответствующие составные присваивания, то распространенной идиомой является реализация первых через вторые
class SomeClass {
SomeClass& operator +=(const SomeClass &rhs) { value += rhs.value; return *this; }
friend SomeClass operator +(SomeClass lhs, const SomeClass &rhs) { // Обратите внимание, что `lhs` передается по значению lhs += rhs; return lhs; } ... };

"Симметричные" операторы лучше задавать самостоятельной функцией, а не функцией-членом для того, чтобы такие операторы вели себя "симметрично" по отношению к неявным приведениям типов аргументов.
Например, если задать бинарный оператор + так, как показано в моем первом примере, т.е. функцией-членом, то будет наблюдаться следующая неприятная асимметрия
SomeClass a(1), b(2);
a = b + 1; // <- OK a = 1 + b; // <- Ошибка: нет такого оператора `+`
т.е. такой оператор не будет рассматриваться в качестве кандидата в выражении 1 + b. Функции-члены берутся на рассмотрение только из левого операнда. А в случае 1 + b левым операндом является 1, у которой никаких функций-членов нет вообще.
Но как только вы сделаете свой бинарный + самостоятельной функцией, он будет симметричным образом применим в обоих вариантах.

Комментариев нет:

Отправить комментарий