Home c++ Overloading the addition operator

Overloading the addition operator

Author

Date

Category

It would seem that I am writing everything correctly, the addition of two cats will give a new cat, whose value will be the sum of their values. But I get two errors.

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.

Answer 1, authority 100%

Who will declare the return type?

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

Only this is – firstly, not a decision, and secondly, a bad decision.

Not a solution – because in general the + operator is binary. It adds two values, and you have three added together – also your object (this ).

Cat * operator + (Cat b) {
  return new Cat (this- & gt; value + b.value);
}

Now this is the solution. But bad. Because it does not return a cat, but a pointer to it. As a result, the responsibility for the mandatory storage of the value and the freeing of memory in the future is shifted to the calling function.

This can be avoided by returning a finished cat:

Cat operator + (Cat b) {
  return Cat (this- & gt; value + b.value);
}

And the last thing. If the cat is big (not in the sense of value , but takes up a lot of memory), then it makes sense to transfer it to the operator not by value, but by reference:

Cat operator + (const Cat & amp; b) {
  return Cat (this- & gt; value + b.value);
}

Answer 2, authority 20%

A binary operator, such as the addition operator operator + , must be defined either as a non-static member function with one parameter, or as a non-member function with two parameters.

Compiler messages given to you

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);
        ^ ~~~~~~~~~~~~~~~~~~~~~~~~~

indicates that the operator you defined does not have a return type.

Only conversion functions can have a return type. In the case of the addition operator, you must specify the return type.

When adding two objects of a class, it makes no sense to return a pointer to the object. In this case, you will not be able to chain the addition operators without using additional operators, and, moreover, it can lead to a memory leak.

The operator must return the object itself, either with or without the const qualifier.

As mentioned above, a statement can be declared as a non-static member function of a class with one parameter.

In this case, the operator + might look like this

class Cat
{
private:
  int value = 1;
public:
  Cat (int _value)
  {
    value = _value;
  }
  Cat operator + (const Cat & amp; a) const
  {
    return Cat (this- & gt; value + a.value);
  }
};

either as

class Cat
{
private:
  int value = 1;
public:
  Cat (int _value)
  {
    value = _value;
  }
  const Cat operator + (const Cat & amp; a) const
  {
    return Cat (this- & gt; value + a.value);
  }
};

You could also overload the operator for rvalue references, such as

Cat operator + (const Cat & amp; & amp; a) const
{
  return Cat (this- & gt; value + a.value);
}

or

Cat operator + (Cat & amp; & amp; a) const
{
  return Cat (this- & gt; value + a.value);
}

but for such a simple class that does not take up large resources, this does not matter.

Note the presence of the condt qualifier after the parameter list. This means that the object itself, which will be present on the left side of the operator, will not change, as well as the right object, since the corresponding operator parameter is also defined with the qualifier const .

Keep in mind, since the class constructor is declared as a converting constructor, in this case you can add objects of the Cat class with numbers. For example,

# include & lt; iostream & gt;
class Cat
{
private:
  int value = 1;
public:
  Cat (int _value)
  {
    value = _value;
  }
  const Cat operator + (const Cat & amp; a) const
  {
    return Cat (this- & gt; value + a.value);
  }
};
int main ()
{
  Cat c1 (10);
  c1 + 5.5;
  return 0;
}

It is up to you to decide how justified this is, based on the meaning of this addition operator. If you do not want to allow such an implicit conversion from a number to an object of the Cat class, then you can declare the constructor as explicit. In this case, the program will not compile if it attempts to add an object of class Cat with a number.

# include & lt; iostream & gt;
class Cat
{
private:
  int value = 1;
public:
  explicit Cat (int _value)
  {
    value = _value;
  }
  const Cat operator + (const Cat & amp; a) const
  {
    return Cat (this- & gt; value + a.value);
  }
};
int main ()
{
  Cat c1 (10);
  c1 + 5.5;
  return 0;
}

For this program, the compiler will give an error message like the following

prog.cpp: 24: 5: error: no match for 'operator +' (operand types are 'Cat' and 'double')
 c1 + 5.5;
   ^

The second way to declare this operator is to declare it as a function that is not a member of the class. Since this function must have access to the private member of the value class, it must be declared as a friendly function of the class.

You can define the function itself both inside the class definition and outside it.

For example,

# include & lt; iostream & gt;
class Cat
{
private:
  int value = 1;
public:
  Cat (int _value)
  {
    value = _value;
  }
  friend const Cat operator + (const Cat & amp; a, const Cat & amp; b)
  {
    return Cat (a.value + b.value);
  }
};
int main ()
{
  Cat c1 (10);
  Cat c2 (5);
  Cat c3 = c1 + c2;
  return 0;
}

Keep in mind that it is a bad idea to declare variables that start with an underscore. In general, such names beginning with an underscore are reserved for the compiler.

It is customary to assign names to constructor parameters that correspond to the names of class members. In this case, you can immediately see which parameter is initializing which class member. So you could define a constructor like this

Cat (int value): value (value)
{
}

If a member of the value class cannot take negative values, then it is better to declare it and the corresponding constructor parameter as having the type unsigned int .


Answer 3

Using friend :

# include & lt; iostream & gt; 
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 & amp; lF, Foo const & amp; 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 & lt; & lt; f3.getA () & lt; & lt; std :: endl;
   return 0;
}

Programmers, Start Your Engines!

Why spend time searching for the correct question and then entering your answer when you can find it in a second? That's what CompuTicket is all about! Here you'll find thousands of questions and answers from hundreds of computer languages.

Recent questions