Зміст курсу
C++ ООП
C++ ООП
Перевантаження Операторів
Перевантаження операторів — це потужна можливість у мовах об'єктно-орієнтованого програмування, яка дозволяє перевизначати поведінку операторів для користувацьких classes
. Завдяки перевантаженню операторів можна створювати власні реалізації операцій для об'єктів вашого class
, що забезпечує більш інтуїтивний та виразний код.
Синтаксис перевантаження операторів
Перевантаження досягається шляхом визначення спеціальних функцій-членів або дружніх функцій, які реалізують необхідну поведінку для оператора. Синтаксис перевантаження операторів залежить від оператора, який ви хочете перевантажити. Загальний вигляд виглядає так:
overloading.h
return_type operator operator_symbol(parameters);
Синтаксис, а точніше, кількість параметрів і тип, що повертається, також залежать від того, чи перевантажуєте ви оператори як функції-члени або як дружні функції.
Member.h
Friend.h
class Example { public: Example operator+ (const Example& other) { // Define behavior for operator+ } };
Таблиця операторів, які можна перевантажити
Нижче наведено таблицю всіх операторів, які можна перевантажити. Однак важливо зазначити, що хоча перевантаження цих операторів можливе, зовсім не обов'язково перевантажувати всі з них для ваших classes
.
Перевантаження операторів вставки у потік
Щоб перевантажити оператори <<
та >>
для class
, зазвичай визначають функцію-друга або метод-член, який приймає потік виводу (std::ostream&
) або потік вводу (std::istream&
) як лівий операнд і об'єкт вашого class
як правий операнд. Ця функція форматує дані об'єкта та передає їх у потік або з потоку.
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; }
Перевантаження операторів вставки у потік, таких як <<
, дозволяє визначити власну поведінку виводу для об'єктів ваших classes
при передачі їх у потік виводу, наприклад, у std::cout
. Ця можливість особливо корисна для підвищення читабельності та зручності використання вашого коду при роботі з власними типами даних.
Перевантаження оператора <<
для потоку виводу є більш поширеним, ніж перевантаження оператора >>
для потоку вводу, оскільки операції вводу з >>
можуть бути більш схильними до помилок.
Перевантаження арифметичних операторів
Можна перевантажувати й інші арифметичні оператори (-
, *
, /
, %
) аналогічним чином для виконання користувацьких операцій з вашими власними типами.
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; }
Оператор перевантажується як метод-член Point
class
. Він приймає інший об'єкт типу Point
як параметр і повертає новий об'єкт типу Point
, який представляє суму двох точок. Оператор +
можна замінити на -
, *
, /
або %
з відповідними змінами в логіці реалізації.
Інкремент і декремент
Як префіксний, так і постфіксний інкремент і декремент (++
та --
) можна перевантажити для користувацьких classes
. Почнемо з префіксної версії:
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; }
Оператор декременту перевантажується аналогічно оператору інкременту, використовуючи оператор --
та операцію віднімання.
Як бачите, перевантаження префіксних операторів інкременту та декременту є простим. Однак із постфіксними версіями виникають складнощі. Зверніть увагу, що існує різниця в порядку виконання між префіксними та постфіксними операціями.
Під час перевантаження постфіксних операторів інкременту та декременту також необхідно реалізувати особливість порядку виконання. Це виглядатиме приблизно так:
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; }
Цілий параметр передається без імені лише для того, щоб повідомити компілятору, що перевантажується оператор постфіксного інкременту. Це необхідно, оскільки оголошення для префіксного та постфіксного операторів інакше ідентичні.
У реалізації оператора постфіксного інкременту початкове значення об'єкта зберігається у тимчасовій змінній (temp
). Поточний об'єкт потім інкрементується, але оператор повертає значення, збережене у temp
. Це означає, що змінна інкрементується, але зміна набуває чинності лише у наступному виразі, оскільки оператор повертає початкове значення.
Дякуємо за ваш відгук!