#cpp
Как определить возвращаемое значение при перегрузке операторов ? В каком случае следует вернуть ссылку на объект, а в каком его побитовую копию ? update: #includeusing 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; }
Ответы
Ответ 1
Операторы бывают двух типов: операторы 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, у которой никаких функций-членов нет вообще. Но как только вы сделаете свой бинарный + самостоятельной функцией, он будет симметричным образом применим в обоих вариантах.Ответ 2
Не ясно для чего этот класс, но в нём есть несколько недостатков. Во-первых, хоть и было бы бессмысленным в вашем примере, но старайтесь всегда принимать во внимание rule of three - тоесть кроме перегрузки оператора = также определите конструктор копии и деструктор. Во-вторвых используйте списки инициализации, вместо инициализации в теле конструктора: SomeClass(int value): value(value) {} А возвращать здесь можете и по ссылке здесь, зачем лишний раз копировать объект ? SomeClass& operator + (const SomeClass &rhs) { value += rhs.value; return *this; } p.s. да и вобще, мой вам совет, реализуйте например класс String, или класс для работы с матрицами - тогда подобные вопроса по немногу начнут отпадать
Комментариев нет:
Отправить комментарий