Course Content
C++ OOP
C++ OOP
Operator Overloading
Operator overloading is a powerful feature in object-oriented programming languages that allows you to redefine the behavior of operators for user-defined classes
. By overloading operators, you can provide custom implementations for operations involving objects of your class
, enabling more intuitive and expressive code.
The syntax of operator overloading
Overloading s achieved by defining special member functions or friend functions that implement the desired behavior for the operator. The syntax for overloading operators varies depending on operator you want to overload. The general one looks like this:
overloading.h
return_type operator operator_symbol(parameters);
The syntax, or more specifically, the number of parameters and returning type, also depends on whether you're overloading operators as member functions or friend functions.
Member.h
Friend.h
class Example { public: Example operator+ (const Example& other) { // Define behavior for operator+ } };
The table of operators that can be overloaded
Here is a table listing all the operators that can be overloaded. However, it's important to note that while it's possible to overload these operators, it is by no means necessary to overload all of them for your classes
.
Overloading stream insertion operators
To overload the <<
and >>
operators for a class
, you typically define a friend function or a member function that takes an output stream (std::ostream&
) or input stream (std::istream&
) as the left operand and an object of your class
as the right operand. This function then formats the object's data and streams it to or from the stream.
Point.h
class Point { public: friend std::ostream& operator<<(std::ostream& out, const Point& point); friend std::istream& operator>>(std::istream& in, Point& point); private: int x, y; }; std::ostream& operator<<(std::ostream& out, const Point& point) { return out << "x: " << point.x << ", y: " << point.y << std::endl; } std::istream& operator>>(std::istream& in, Point& point) { return in >> point.x >> point.y; }
Overloading stream insertion operators, such as <<
, allows you to define custom output behavior for objects of your classes
when they are streamed to an output stream like std::cout
. This feature is especially useful for enhancing the readability and usability of your code when working with custom data types.
Overloading the <<
operator for output stream is more common than overloading the >>
operator for input stream, because input operations with >>
can be more error-prone.
Overloading arithmetic operators
You can overload other arithmetic operators (-
, *
, /
, %
) in a similar manner to perform custom operations with your user-defined types.
main.cpp
#include <iostream> class Point { public: Point(int xCoord, int yCoord) : x(xCoord), y(yCoord) {} Point operator+(const Point& other) { return Point(x + other.x, y + other.y); } int getX() { return x; } int getY() { return y; } private: int x, y; }; int main() { Point p = Point(2, 4) + Point(2, 6); std::cout << p.getX() << ' ' << p.getY() << std::endl; }
The operator is overloaded as a member function of the Point
class
. It takes another Point
object as a parameter and returns a new Point
object that represents the sum of the two points. The +
operator can be replaced with -
, *
, /
, or %
, with corresponding adjustments to the implementation logic.
Increment and Decrement
Both the prefix and postfix increment and decrement operators (++
and --
) can be overloaded for custom classes
. Let's begin with the prefix version:
main.cpp
#include <iostream> class Point { public: Point(int xCoord, int yCoord) : x(xCoord), y(yCoord) {} // Prefix increment operator (++point) Point& operator++() { ++x; ++y; return *this; } int getX() { return x; } int getY() { return y; } private: int x, y; }; int main() { Point p(2, 2); ++p; std::cout << p.getX() << ' ' << p.getY() << std::endl; }
The decrement operator is overloaded in a similar manner to the increment operator, using the --
operator and the subtraction operation.
As you can see, overloading the prefix increment and decrement operators is straightforward. However, it gets trickier with the postfix versions. Remember, thereβs a difference in the order of execution between prefix and postfix operations.
When overloading postfix increment and decrement we also has to implement the order of execution feature. It will look something like this:
main.cpp
#include <iostream> class Point { public: Point(int xCoord, int yCoord) : x(xCoord), y(yCoord) {} // Postfix increment operator (point++) Point operator++(int) { Point temp = *this; // Creating a temp variable ++(this->x); // Incrementing original Point's x ++(this->y); // Incrementing original Point's y return temp; // Returning created temp variable } int getX() { return x; } int getY() { return y; } private: int x, y; }; int main() { Point p(2, 2); p++; std::cout << p.getX() << ' ' << p.getY() << std::endl; }
The integer parameter is passed without a name only to signal to the compiler that the postfix increment operator is being overloaded. This is necessary because the declarations for prefix and postfix operators are otherwise identical.
In the postfix increment operator implementation, the original value of the object is stored in a temporary variable (temp
). The current object is then incremented, but the operator returns the value stored in temp
. This means the variable is incremented, but the change takes effect only in the next expression, as the operator returns the original value.
Thanks for your feedback!