Урок №142. Перевантаження операторів через методи класу

  Юрій  | 

  Оновл. 25 Вер 2021  | 

 180

Перевантаження операторів через методи класу дуже схоже на перевантаження операторів через дружні функції. Але при перевантаженні оператора через метод класу лівим операндом стає неявний об’єкт, на який вказує прихований вказівник *this.

Перевантаження операторів через методи класів

Згадаймо, як виглядає перевантаження оператора через дружню функцію:

Конвертація перевантаження через дружню функцію в перевантаження через метод класу досить-таки проста:

   Оператор, який потрібно перевантажити, визначається як метод класу замість дружньої функції (Dollars::operator+ замість friend operator+).

   Лівий параметр з функції перевантаження викидається, замість нього — неявний об’єкт, на який вказує вказівник *this.

   Всередині тіла функції перевантаження всі посилання на лівий параметр можуть бути видалені (наприклад, dollars.m_dollars стає m_dollars, який неявно посилається на поточний об’єкт за допомогою вказівника *this).

Тепер те ж саме перевантаження оператора +, але вже через метод класу:

Зверніть увагу, використання оператора + не змінюється (в обох випадках dollars1 + 3), але реалізація відрізняється. Наша дружня функція з двома параметрами стає методом класу з одним параметром, причому лівий параметр в перевантаженні через дружню функцію (&dollars), в перевантаженні через метод класу стає неявним об’єктом, на який вказує вказівник *this.

Розглянемо детально, як обробляється вираз dollars1 + 3.

У перевантаженні через дружню функцію вираз dollars1 + 3 призводить до виклику функції operator+(dollars1, 3). Тут є два параметри.

У перевантаженні через метод класу вираз dollars1 + 3 призводить до виклику dollars1.operator+(3). Зверніть увагу, тут вже один явний параметр, а dollars1 використовується як префікс до operator+. Цей префікс компілятор неявно конвертує в прихований лівий параметр, на який вказує вказівник *this. Таким чином, dollars1.operator+(3) стає викликом operator+(&dollars1, 3), що майже ідентично перевантаженню через дружню функцію.

Отже, якщо ми можемо перевантажити оператор через дружню функцію або через метод класу, то що тоді вибрати? Перш ніж ви отримаєте відповідь на це питання, вам потрібно дізнатися ще кілька деталей.

Не все можна перевантажити через дружні функції

Оператори присвоювання (=), індексу ([]), виклику функції (()) і вибору члену (->) перевантажуються через методи класу — це вимога мови C++.

Не все можна перевантажити через методи класу

На уроці про перевантаження операторів вводу і виводу ми перевантажували оператор виводу << для класу Point через дружню функцію:

Однак через метод класу перевантажити оператор << ми не зможемо. Чому? Тому що при перевантаженні через метод класу в якості лівого операнду використовується поточний об’єкт. В цьому випадку лівим операндом є об’єкт типу std::ostream. std::ostream є частиною Стандартної бібліотеки C++. Ми не можемо використовувати std::ostream в якості лівого неявного параметра, на який би вказував прихований вказівник *this, так як вказівник *this може вказувати тільки на поточний об’єкт поточного класу, члени якого ми можемо змінити, тому перевантаження оператора << повинне здійснюватися через дружню функцію.

Аналогічно, хоча ми можемо перевантажити operator+(Dollars, int) через метод класу (як ми робили вище), ми не можемо перевантажити operator+(int, Dollars) через метод класу, оскільки int тепер є лівим операндом, на який вказівник *this вказувати не може.

Перевантаження операторів через методи класу не використовується, якщо лівий операнд не є класом (наприклад, int), або це клас, який ми не можемо змінити (наприклад, std::ostream).

Який спосіб перевантаження і коли слід використовувати?

У більшості випадків мова C++ дозволяє вибирати самостійно спосіб перевантаження операторів.

Але при роботі з бінарними операторами, які не змінюють лівий операнд (наприклад, operator+()), зазвичай використовується перевантаження через звичайну або дружню функцію, оскільки таке перевантаження працює для всіх типів даних параметрів (навіть якщо лівий операнд не є об’єктом класу або є об’єктом класу, який змінити не можна). Перевантаження через звичайну/дружню функцію має додаткову перевагу «симетрії», так як всі операнди стають явними параметрами (а не як у перевантаженні через метод класу, коли лівий операнд стає неявним об’єктом, на який вказує вказівник *this).

При роботі з бінарними операторами, які змінюють лівий операнд (наприклад, operator+=()), зазвичай використовується перевантаження через методи класу. У цих випадках лівим операндом завжди є об’єкт класу, на який вказує прихований вказівник *this.

Унарні оператори зазвичай теж перевантажуються через методи класу, так як в такому випадку параметри не використовуються взагалі.

Тому:

   Для операторів присвоювання (=), індексу ([]), виклику функції (()) або вибору члена (->) використовуйте перевантаження через методи класу.

   Для унарних операторів використовуйте перевантаження через методи класу.

   Для перевантаження бінарних операторів, які змінюють лівий операнд (наприклад, operator+=()) використовуйте перевантаження через методи класу, якщо це можливо.

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

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

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

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

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