Зміст курсу
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:
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. Consider the following two examples:
Member
Friend
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 is s possible to overload these operators, it is completely not necessary to overload all of them for your classes.
() | function() | |
[] | array[index] | |
++ -- | ++a , a-- , etc. | |
+ - * / % | a + b , a - b , etc. | |
+= -= *= /= | a += b , a -= b , etc. | |
== != < > | a == b , a < b , etc. | |
<< >> | cout << a , cin >> a |
We will focus on the most commonly used operators, including stream insertion and extraction, arithmetic operators, increment and decrement operators.
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 the output stream.
Point
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, such as std::cout
. This feature is particularly useful for enhancing the readability and usability of your code when dealing with custom data types.
Note
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
#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; }
In this example, we overloade 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. You can change +
for -
, *
, /
, %
and adjust a implementation logic a bit.
Increment and Decrement
You can overload both the prefix and postfix increment and decrement operators (++
and --
) for your custom classes. Let's begin with prefix:
main
#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; }
Note
The decrement operator is overloaded in a similar manner to the increment operator, with the use of the
--
operator and subtraction operation.
As you see there is nothing hard in overloading prefix increment and decrement. However it is gets trickier when it comes to postfix one. As you remember there is an order of execution of postfix and prefix increment. Let's assume we have variable x
with a value 5
.
++Prefix | ++x | 6 | |
Postfix++ | x++ | 5 | |
--Prefix | --x | 4 | |
Postfix-- | x-- | 5 |
When overloading postfix increment and decrement we also has to implement the order of execution feature. It will look something like this:
main
#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; }
Note
The integer parameter is passed without an argument name solely to signify to the compiler that it is the postfix increment operator being overloaded. It is necessary because the declaration of prefix and postfix operators does not differ in any other way.
In the postfix increment operator implementation, the original value of the object is stored in a temporary variable (temp). While the current object is then incremented, the value returned by the operator is the one stored in temp. So, the variable is incremented, but the incrementation takes effect only in the next expression, as the operator returns the original value.
Дякуємо за ваш відгук!