Assume we have some complex number class with real and imaginary components. When we use some operator between objects:

class Complex {
	private:
		double real;
		double im;
	public:
		// constructors, and so on
		Complex operator+(const Complex& rhs) const {
			Complex w(real + rhs.real, im + rhs.im);
			return w;
		}
		friend ostream& operator<<(ostream& os, Complex rhs);
}
 
 ostream& operator<<(ostream& os, Complex rhs) { // not Complex::
	os << rhs.real << " + " << rhs.im << "j";
	return os;
}
 
int main (void) {
	Complex x(3,5), y(4,6),z;
	z = x + y; // equivalent to x.operator+(y)
}

A generally good step to take is to pass by reference into the operator+ method, mainly for memory efficiency (since the object would have to be copied over otherwise, consider what would happen if we had many objects). operator is a keyword in C++.

We also want to pass the complex in as a const, just so that we don’t modify rhs at all (we’d get a compile-time error if we did). Observe that we also have another const within the method definition: this tells the compiler that we’re not trying to change any of the object attributes we pass in.

We can also overload the << operator (for cout). But we can’t do it as a member of the Complex class, but instead of the ostream class, because cout is an object of ostream. But we can’t modify the iostream library, so we implement it outside of all functions as a friend function. Observe that we also return of type ostream as well. We’re not allowed to pass or return by value for cout though, so we pass and return by reference.

Addendums

Note that we have (x+y) as what is essentially a brand-new Complex object. By default, an operator= method is implemented for every class. So we don’t have to define it again. It sets all data members in the object to the data members of the right-hand side object.

When we implement this, is it a void method? Not necessarily. Recall that this is legal in C++: a = b = c; which assigns the right-hand side first, then slowly works its way to the left. So it intuitively makes sense to have a return type.

Complex& Complex::operator=(const Complex &rhs) { // return by reference
	real = rhs.real;
	im = rhs.im;
	return *this; // dereferences self pointer
}