Overloaded operators

Table of Contents


When creating our own classes, we may want to make certain operators mean certain things when used with our class. By default, we can't use math operators (+, -, /, *), stream operators (<<, >>), relational operators (<, <=, >, >=, ==, !=), the subscript operator [], and the assignment operator (=) with the classes we create. In order to do so, we would have to use operator overloading to define our own functions and how these operators work on our class.

For example, we could define our own Fraction class, without overloaded arithmetic operators we would have to do math like this:

Frac frac1( 1, 2 );       // 1/2
Frac frac2( 2, 3 );       // 2/3
Frac sum = frac1.Add( frac2 );  // Add 1/2 + 2/3

When we set up the arithmetic operators so you could simply do:

Frac frac1( 1, 2 );     // 1/2
Frac frac2( 2, 3 );     // 2/3
Frac sum = frac1 + frac2; // Add 1/2 + 2/3

…Which is much more intuitive.


1. Arithmetic operators

Arithmetic operators will be friend functions of our class. This is because it will take two objects and return a third, so it doesn't "neatly" fit inside one class as a member function.

Friend arithmetic function declarations:

class MyClass
{
public:
  friend MyClass operator+( const MyClass& item1,
                            const MyClass& item2 );
  friend MyClass operator-( const MyClass& item1,
                            const MyClass& item2 );
  friend MyClass operator/( const MyClass& item1,
                            const MyClass& item2 );
  friend MyClass operator*( const MyClass& item1,
                            const MyClass& item2 );
};

Then the function definition would go in a source file (it could go in MyClass.cpp as well, but it's not a member function so don't prefix MyClass:: on these functions).

MyClass operator+( const MyClass& item1,
                   const MyClass& item2 )
{
  MyClass sum;
  sum.memberA = item1.memberA + item2.memberA;
  return sum;
}

2. Relational operators

Relational operators also operate on two objects so these are also friend functions.

Friend relational function declarations:

class MyClass
{
public:
  friend bool operator==( const MyClass& item1,
                          const MyClass& item2 );
  friend bool operator!=( const MyClass& item1,
                          const MyClass& item2 );
  friend bool operator<( const MyClass& item1,
                         const MyClass& item2 );
  friend bool operator<=( const MyClass& item1,
                          const MyClass& item2 );
  friend bool operator>( const MyClass& item1,
                         const MyClass& item2 );
  friend bool operator>=( const MyClass& item1,
                          const MyClass& item2 );
};

Function definition:

bool operator==( const MyClass& item1,
                 const MyClass& item2 )
{
  return ( item1.memberA == item2.memberA );
}

3. Stream operators

Stream operators will take in a stream reference and the object to input or output and return the stream reference of the modified stream. These are also friend functions. We use the ostream and istream classes as the return type and parameter since this is the parent class of cout/cin and ofstream/ifstream, as well as other stream types.

Friend stream function declarations:

class MyClass
{
public:
  friend ostream& operator<<( ostream& out,
                              MyClass& item );

  friend istream& operator>>( istream& in,
                              MyClass& item );
};

Function definition:

ostream& operator<<( ostream& out, MyClass& item );
{
  out << item.memberA;
  return out;
}

istream& operator>>( istream& in, MyClass& item );
{
  in >> item.memberA;
  return in;
}

You can output or input multiple items in these functions so that you can get input for all class members, or output all class members, in one statement.


4. Subscript operator

The subscript operator is usually used when creating a type of list structure so that we can access an element at some position. This function is a member function of the class.

Member function declaration:

class MyClass
{
public:
  string& operator[] ( const int index );

private:
  string m_data[100];
};

Function definition:

string& MyClass::operator[]( const int index )
{
  return m_data[ index ];
}

5. Assignment operator

The assignment operator is similar to the copy constructor in that we are telling our class how it will copy the data from another object of the same class. It's up to us to decide which members to copy over as part of this process.

Member function declaration:

class MyClass
{
public:
  MyClass& operator=( const MyClass& other );

private:
  int a;
  float b;
};

Function definition:

MyClass& MyClass::operator=( const MyClass& other )
{
  if ( this == &other ) { return *this; }

  a = other.a;
  b = other.b;

  return *this;
}

Note the if statement in our assignment operator. This is checking to see if the memory address of "this" class is the same as the "other" class being passed in. We don't want to make a copy if these are actually the same object, so in that case we return "this" (but it must be de-referenced).

Otherwise, we can copy whichever member variables we would like, and make sure to return "this" at the end (also de-referenced).


Author: Rachel Wil Sha Singh

Created: 2023-10-05 Thu 22:11

Validate