C++ operator overload
Operator overloading lets a class define how normal C++ operators work with objects of that class.
For example, if Number is a class, C++ does not automatically know what this
means:
By writing operator+, we teach the class how + should behave.
Full example
Output:
Member operator overload
A member operator is written inside the class.
This means:
is translated by the compiler into:
So with a member operator:
- the left side is the current object,
*this - the right side is passed as a function argument
- the function can access private members of the class
Inside operator+, the object on the left side is a, and the object on the
right side is b.
This is the same idea as:
but inside the member function, a.value_ is written as just value_.
Assignment operator must be a member
Some operators must be overloaded as member functions.
The assignment operator is one of them:
This code:
calls:
The function returns Number& so chained assignment can work:
The expression b = Number(30) returns b, then a = b can run.
Important operators that must be members include:
operator=operator[]operator()operator->
Use a member function for these operators because C++ requires it.
Non-member operator overload
A non-member operator is written outside the class.
This means:
is translated by the compiler into:
With a non-member operator:
- both sides are normal function arguments
- there is no
this - the function cannot access private members unless it is a
friend - it usually uses public functions like
get_value()
In this example, operator- is outside the class, so it cannot directly use
value_.
This would not compile:
So the function uses the public getter:
Non-member operator for output
The stream operator << is normally written as a non-member.
This code:
calls:
It should not be a member of Number, because the left side is not a Number.
The left side is std::cout, which is a std::ostream.
If operator<< were a member of Number, the syntax would need to look like
this:
So operator<< is almost always a non-member operator.
It returns std::ostream& so printing can be chained:
First this runs:
Then the same stream is returned, so this can run next:
Then the stream is returned again, so this can run:
Non-member operator for equality
The equality operator is also often written as a non-member.
This code:
calls:
For comparison operators, non-member functions are often preferred because the left and right sides are treated equally.
Member vs non-member
Use a member operator when:
- C++ requires the operator to be a member, like
=,[],(), or-> - the operator naturally modifies the left object, like
+= - the operation is strongly tied to the class internals
Use a non-member operator when:
- the left side is not your class, like
std::cout << number - both sides should be treated equally, like
a == b - you want conversions to work on the left side too
- the operator can be implemented using the public interface
Example:
This is a good non-member because subtraction uses two Number objects equally.
Example:
This must be a member because assignment changes the object on the left side.
Implicit conversion and non-member operators
Implicit conversion on the left side only works with a non-member operator.
In the main example, the constructor is marked explicit:
That is usually a good choice because it prevents accidental conversions from
int to Number.
For this example only, remove explicit so C++ is allowed to convert an int
into a Number automatically.
Member operator
With a member operator+:
This works:
The compiler sees:
The left side is already a Number, and the right side 5 can be converted to
Number.
But this does not work:
For a member operator, the left side must already be the class object, because C++ needs something to call the member function on.
The compiler would need this:
So the compiler cannot use Number::operator+ here.
Non-member operator
With a non-member operator+:
Both directions work:
Because operator+ is a normal function, both arguments can use implicit
conversion.
That is why symmetric operators like +, -, ==, and != are often better
as non-member functions.
Conversion operator
A conversion operator lets an object convert itself to another type.
The syntax is:
Example:
Output:
This function:
means:
a
Numberobject can be converted to anint.
So this line:
is similar to:
Notice that a conversion operator has no return type before the function name.
The target type is written after the word operator.
Do not write this:
Write this:
Convert to another class type
A conversion operator can also convert an object to another class type.
Example:
Output:
This function:
means:
a
Numberobject can be converted to aTextobject.
So this line:
is similar to:
The conversion operator belongs to the source type.
In this example, Number knows how to convert itself into Text, so
operator Text() is written inside Number.
Explicit conversion operator
Implicit conversion can sometimes make code unclear.
You can make the conversion operator explicit:
Now this does not work:
You must write the conversion clearly:
Use explicit operator type() when automatic conversion could surprise the
reader.
Const correctness
This operator is marked const:
The final const means operator+ does not modify the left object.
That is important because addition should create a new value:
It should not change a or b.
The parameter is also a const reference:
This avoids copying other and promises not to modify it.
Friend functions
Sometimes a non-member operator needs access to private data.
One option is to make it a friend.
A friend function is not a member function, but it is allowed to access private
members.
This line inside the class is only a declaration:
It tells C++:
this non-member function is allowed to access private members of
Number.
The function body can be written outside the class:
You can also write the body directly inside the class:
This is still a non-member function. The friend keyword gives access to private
members, but it does not make the function a class member.
Use friend when the operator really belongs outside the class but needs direct
access to private state.
Do not use friend automatically. If the operator can be cleanly implemented
with public getters, that is often simpler.
Common rule
A practical rule:
- operators that change the left object are often members
- symmetric operators are often non-members
- stream operators are non-members
- assignment-like operators are members
In this example:
The syntax looks similar, but the function call behind the syntax is different.
Demo: conversion operation
This example shows a conversion operator from one class type to another class type.
Output:
This function is the conversion operator:
It means:
an
Opobject can be converted to anAobject.
This line:
first converts op to A:
That cast calls:
After the conversion, the expression becomes similar to:
Then C++ calls the operator<< function for A:
So the full flow is:
(A)opcallsOp::operator A() constoperator A()returns a newAobjectcout << A_objectcallsoperator<<(ostream&, const A&)operator<<prints1
The conversion operator belongs to the source type, so operator A() is written
inside Op.
The stream operator belongs outside A because the left side is cout, not an
A object.