Страницы

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

пятница, 13 декабря 2019 г.

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

#cpp


Как определить возвращаемое значение при перегрузке операторов ? В каком случае следует
вернуть ссылку на объект, а в каком его побитовую копию ?

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;
}

    


Ответы

Ответ 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, или класс для работы с матрицами - тогда подобные вопроса по немногу начнут отпадать

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

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