Урок №138. Перевантаження операторів

  Юрій  | 

  Оновл. 15 Лют 2021  | 

 22

Ми вже знаємо, що перевантаження функцій забезпечує механізм створення і виконання викликів функцій з одним і тим же ім’ям, але з різними параметрами. Це дозволяє одній функції працювати з декількома різними типами даних (без необхідності придумувати унікальні імена для кожної з функцій).

У мові C++ оператори реалізовані у вигляді функцій. Використовуючи перевантаження функції оператора, ви можете визначити свої власні версії операторів, які працюватимуть з різними типами даних (включаючи класи). Використання перевантаження функції для перевантаження оператора називається перевантаженням оператора.

Оператори, як функції

Розглянемо наступний фрагмент коду:

Тут компілятор використовує вбудовану версію оператора плюс (+) для цілочисельних операндів — ця функція додасть два цілочисельних значення (a і b), і поверне цілочисельний результат. Коли ви бачите вираз a + b, то думайте про нього, як про виклик функції operator+(a, b) (де operator+ є ім’ям функції).

Тепер розглянемо наступний фрагмент коду:

Компілятор також надасть вбудовану версію оператора плюс (+) для операндів типу double. Вираз m + p призведе до виклику функції operator+(m, p), а, завдяки перевантаженню оператора, викличеться версія double (замість версії int).

Тепер розглянемо, що відбудеться, якщо ми спробуємо додати два об’єкти класу:

Як ви думаєте, яким буде результат? Напевно, вивід рядка Hello, World!? Ні, результатом буде помилка, так як клас Mystring є користувацьким типом даних, а компілятор не має вбудованої версії operator+() для використання з операндами Mystring. Для того, щоб зробити те, що ми хочемо, нам доведеться написати свою версію функції operator+() і вказати в ній алгоритм роботи з операндами типу Mystring. Те, як це зробити в коді, ми розглянемо на наступному уроці.

Виклик перевантажених операторів

При обробці виразу з оператором компілятор використовує наступні алгоритми дій:

   Якщо всі операнди є фундаментальних типів даних, то викликати слід вбудовані відповідні версії операторів (якщо такі існують). Якщо таких не існує, то компілятор видасть помилку.

   Якщо будь-який з операндів є користувацького типу даних (наприклад, об’єкт класу або типу перерахування), то компілятор шукатиме версію оператора, яка працює з таким типом даних. Якщо компілятор не знайде нічого підходящого, то спробує виконати конвертацію одного або декількох операндів користувацького типу даних в фундаментальні типи даних, щоб таким чином він міг використати відповідний вбудований оператор. Якщо це не спрацює — компілятор видасть помилку.

Обмеження в перевантаженні операторів

По-перше, майже будь-який існуючий оператор в мові C++ можна перевантажити. Винятками є:

   тернарний оператор (?:);

   оператор sizeof;

   оператор дозволу області видимості (::);

   оператор вибору члена (.);

   вказівник, як оператор вибору члена (.*).

По-друге, ви можете перевантажити тільки існуючі оператори. Ви не можете створювати нові або перейменовувати існуючі. Наприклад, ви не можете створити оператор ** для виконання операції піднесення до степеня.

По-третє, принаймні один з операндів перевантаженого оператора повинен бути користувацького типу даних. Це означає, що ви не можете перевантажити operator+() для виконання операції додавання значення типу int зі значенням типу double. Однак ви можете перевантажити operator+() для виконання операції додавання значення типу int з об’єктом класу Mystring.

По-четверте, початкову кількість операндів, підтримуваних оператором, змінити неможливо. Тобто з бінарним оператором використовуються тільки два операнда, з унарним — тільки один, з тернарним — тільки три.

Нарешті, всі оператори зберігають свої пріоритет і асоціативність за замовчуванням (незалежно від того, для чого вони використовуються), і це не може бути змінено.

Деякі початківці намагаються перевантажити побітовий оператор XOR (^) для виконання операції піднесення до степеня. Однак в мові C++ у оператора ^ пріоритет нижче, ніж у базових арифметичних операторів (+, -, *, /), і це призведе до некоректної обробки виразів.

В математиці операція піднесення до степеня виконується ДО виконання базових арифметичних операцій, тому 2 + 5 ^ 2 обчислюється як 2 + (5 ^ 2) => 2 + 25 => 27. Однак в мові C++ у базових арифметичних операторів пріоритет вище, ніж у оператора ^, тому 2 + 5 ^ 2 обчислиться як (2 + 5) ^ 2 => 7 ^ 2 => 49.

Вам потрібно буде явно помістити в дужки частину з піднесенням до степеня (наприклад, 2 + (5 ^ 2)) кожен раз, коли ви хочете, щоб вона виконувалася першою, що дуже легко забути і, таким чином, наробити помилок. Тому проводити подібні експерименти не рекомендується.

Примітка: У мові C++ для піднесення до степеня використовується функція pow() з заголовку cmath. У вищенаведеному прикладі з виконанням виразу 2 + 5 ^ 2 в мові C++, мається на увазі, що ви перевантажите побітовий оператор XOR (^) для виконання операції піднесення до степеня.

Правило: При перевантаженні операторів намагайтеся максимально наближено зберігати функціонал операторів з їх початковими призначеннями.

Навіщо використовувати перевантаження операторів? Ви можете перевантажити оператор + для з’єднання об’єктів вашого класу String або для виконання операції додавання двох об’єктів вашого класу Fraction. Ви можете перевантажити оператор << для виводу вашого класу на екран (або запису в файл). Ви можете перевантажити оператор рівності (==) для порівняння двох об’єктів класу і т.д. Подібні застосування роблять перевантаження операторів однією з найкорисніших особливостей мови C++, так як це спрощує процес роботи з класами і відкриває нові можливості.

Оцінити статтю:

1 Зірка2 Зірки3 Зірки4 Зірки5 Зірок (2 оцінок, середня: 5,00 з 5)
Loading...

Залишити відповідь

Ваш E-mail не буде опублікований. Обов'язкові поля відмічені *