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.
А кто будет объявлять тип возвращаемого значения?
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);
}
Бинарный оператор, такой как, например, оператор сложения 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 <iostream>
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 <iostream>
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 <iostream>
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
.
Используя friend
:
#include <iostream>
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;
}
Кофе для программистов: как напиток влияет на продуктивность кодеров?
Рекламные вывески: как привлечь внимание и увеличить продажи
Стратегії та тренди в SMM - Технології, що формують майбутнє сьогодні
Выделенный сервер, что это, для чего нужен и какие характеристики важны?
Современные решения для бизнеса: как облачные и виртуальные технологии меняют рынок
Проблема в следующемНеобходимо работать с бинарными изображениями довольно внушительных размеров (100000x100000 пикселей)
здравствуйте, есть файл, который до этого частично сортировывалсячастично значит файл состоит, допустим, из такого порядка чисел: 1 2 3 3 5 6 6