Как определить возвращаемое значение при перегрузке операторов ? В каком случае следует вернуть ссылку на объект, а в каком его побитовую копию ?
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, у которой никаких функций-членов нет вообще.
Но как только вы сделаете свой бинарный + самостоятельной функцией, он будет симметричным образом применим в обоих вариантах.
Комментариев нет:
Отправить комментарий