Страницы

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

суббота, 28 декабря 2019 г.

Перегрузка оператора сложения

#cpp #классы #function_overloading


class Cat {
private:
    int value = 1;

public:
    Cat(int _value) {
        value = _value;
    }

    operator+(Cat a, Cat b) {
        return new Cat(a.value + b.value);
    }
};


Казалось бы все пишу правильно, сложение двух котов даст нового кота, у которого
value будет суммой их value'ов. Но получаю две ошибки. 

main.cpp:17:5: error: C++ requires a type specifier for all declarations
    operator+(Cat a, Cat b) {
    ^
main.cpp:18:16: error: cannot initialize return object of type 'int' with an rvalue
of type 'Cat *'
        return new Cat(a.value + b.value);
               ^~~~~~~~~~~~~~~~~~~~~~~~~~
2 errors generated.

    


Ответы

Ответ 1



А кто будет объявлять тип возвращаемого значения? Cat* operator+(Cat a, Cat b) { return new Cat(a.value + b.value); } Только это - во-первых, не решение, а во-вторых, плохое решение. Не решение - потому что вообще оператор + бинарный. Он складывает два значения, а у вас в сложении участвуют три - еще и ваш объект (this). Cat* operator+(Cat b) { return new Cat(this->value + b.value); } Теперь это решение. Но плохое. Потому что возвращает не кота, а указатель на него. В результате на вызывающую функцию перекладывается ответственность за обязательное сохранение значения и освобождение в дальнейшем памяти. Этого можно избежать, если возвращать готового кота: Cat operator+(Cat b) { return Cat(this->value + b.value); } И последнее. Если кот большой (не в смысле значения value, а занимает много памяти) - то имеет смысл передавать его в оператор не по значению, а по ссылке: Cat operator+(const Cat& b) { return Cat(this->value + b.value); }

Ответ 2



Бинарный оператор, такой как, например, оператор сложения operator +должен быть определен либо как нестатическая функция - член класса с одним параметром, либо как функция, которая не является членом класса, с двумя параметрами. Выданные вам сообщения компилятора main.cpp:17:5: error: C++ requires a type specifier for all declarations operator+(Cat a, Cat b) { ^ main.cpp:18:16: error: cannot initialize return object of type 'int' with an rvalue of type 'Cat *' return new Cat(a.value + b.value); ^~~~~~~~~~~~~~~~~~~~~~~~~~ говорят о том, что у определенного вами оператора отсутствует тип возвращаемого значения. Тип возвращаемого значения может отсутствовать только у функций преобразования. В случае же оператора сложения вы обязаны указать тип возвращаемого значения. При сложении двух объектов класса не имеет никакого смысла возвращать указатель на объект. В этому случае вы не сможете связывать операторы сложения в цепочку без применения дополнительных операторов, и, более того, это может привести к утечке памяти. Оператор должен возвращать сам объект либо с квалификатором const либо без него. Как уже выше упомянуто, оператор может быть объявлен как нестатическая функция-член класса с одним параметром. В этом случае оператор operator + может выглядеть так class Cat { private: int value = 1; public: Cat(int _value) { value = _value; } Cat operator +( const Cat &a ) const { return Cat( this->value + a.value ); } }; либо как class Cat { private: int value = 1; public: Cat(int _value) { value = _value; } const Cat operator +( const Cat &a ) const { return Cat( this->value + a.value ); } }; Вы могли бы перегрузить оператор также для rvalue-ссылок, как, например, Cat operator +( const Cat &&a ) const { return Cat( this->value + a.value ); } или Cat operator +( Cat &&a ) const { return Cat( this->value + a.value ); } но для такого простого класса, который не захватывает большие ресурсы, это значения не имеет. Обратите внимание на присутствие квалификатора condt после списка параметров. Это говорит о том, что сам объект, который будет присутствовать в левой части от оператора, изменяться не будет, так же, как и правый объект, так как соответствующий ему параметр оператора определен также с квалификатором const. Имейте в виду, так как конструктор класса объявлен как преобразующий конструктор, то вы в этом случае можете складывать объекты класса Cat с числами. Например, #include class Cat { private: int value = 1; public: Cat(int _value) { value = _value; } const Cat operator +( const Cat &a ) const { return Cat( this->value + a.value ); } }; int main() { Cat c1( 10 ); c1 + 5.5; return 0; } Насколько это оправдано - это решать вам, исходя из того, какой смысл вкладывается в этот оператор сложения. Если вы не хотите допускать такого неявного преобразования из числа в объект класса Cat, то вы можете объявить конструктор, как явный. В этом случае программа не будет компилироваться, если имеет попытка сложить объект класса Cat с числом. #include class Cat { private: int value = 1; public: explicit Cat(int _value) { value = _value; } const Cat operator +( const Cat &a ) const { return Cat( this->value + a.value ); } }; int main() { Cat c1( 10 ); c1 + 5.5; return 0; } Для этой программы компилятор выдаст сообщение об ошибке на подобие следующего prog.cpp:24:5: error: no match for 'operator+' (operand types are 'Cat' and 'double') c1 + 5.5; ^ Второй способ объявить этот оператор - это объявить его как функцию, которая не является членом класса. Так как эта функция должна иметь доступ к закрытому члену класса value, то ее нужно будет объявить как дружественную функцию класса. Саму функцию вы можете определить как внутри определения класса, так и вне его. Например, #include class Cat { private: int value = 1; public: Cat(int _value) { value = _value; } friend const Cat operator +( const Cat &a, const Cat &b ) { return Cat( a.value + b.value ); } }; int main() { Cat c1( 10 ); Cat c2( 5 ); Cat c3 = c1 + c2; return 0; } Имейте в виду, что это плохая идея объявлять переменные, начинающиеся с подчеркивания. В общем случае такие имена, начинающиеся с подчеркивания, зарезервированы за компилятором. Принято для параметров конструктора присваивать те имена, которые соответствуют именам членов класса. В этом случае сразу же видно, какой параметр какой член класса инициализирует. Поэтому вы могли бы определить конструктор следующим образом Cat( int value ) : value( value ) { } Если член класса value не может принимать отрицательные значения, то лучше его и соответствующий параметр конструктора объявить, как имеющие тип unsigned int.

Ответ 3



Используя friend: #include class Foo { public: Foo( int p_a ) : m_a( p_a ) {} int getA() const { return m_a; } protected: int m_a; friend Foo operator+( Foo const& lF, Foo const& rF ) { return Foo( lF.m_a + rF.m_a ); } }; int main() { Foo f1( 12 ), f2( 13 ), f4( 14 ); Foo f3 = f1 + f2 + f4; std::cout << f3.getA() << std::endl; return 0; }

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

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