C++/Об'єктно-орієнтовне програмування/Перевантаження операторів
Перевантаження операторів
[ред.]C++. Перевантаження операторів це особливий випадок поліморфізму за якою, деякі або всі оператори, такі як +, = або == поводять себе як поліморфні функції, і як наслідок мають різню поведінку в залежності від типу аргументів, що беруть участь в даній операції. Перевантаження операторів то лише предмет синтаксичного цукру. І його легко емулювати використовуючи виклики функцій.
Розглянемо наступну операцію:
add (a, multiply (b,c))
Використання перевантаження операторів дозволяє мати більш компактний спосіб їх написання, наприклад:
a + b * c
(За умови, що оператор * має вищій пріоритет ніж +.)
Перевантаження операторів може мати не тільки естетичні переваги, оскільки мова програмування дозволяє неявний виклик операторів в деяких обставинах. Критика з приводу використання перевантаження операторів виникає з приводу того, що воно дозволяє мати повну свободу дій і надавати операторам будь-яку функціональність, без жодного контролю чи узгоджування, що можуть задовольнити очікування користувача/читача даного коду. Використання оператора <<
є прикладом цієї проблеми.
// вираз
a << 1;
Поверне подвоєне значення змінної a, якщо a це ціла змінна, але якщо a це вихідний потік, то замість цього цей код запише в нього значення "1". оскільки перевантаження операторів дозволяє програмісту звичайну семантику оператора, завжди вважається хорошою практикою використовувати перевантаження операторів із великою увагою.
Перевантаження оператора, це спосіб надати йому нового значення і поведінки для типу визначеного користувачем. Це відбувається синтаксично так само як і визначення звичайної функції. Базовий синтаксис наступний (де символ @ позначає доступний валідний оператор):
return_type operator@(parameter_list)
{
// ... визначення
}
Не всі оператори можуть бути перевантажені, не можна створити нові оператори, і послідовність виконання, асоціативність або арність операцій не можливо змінити (наприклад оператор ! не можна перетворити на бінарний оператор). Більшість операторів можна перевантажувати у вигляді функції члена класу або функції що не є членом чогось, деякі з них однак, зажди мають визначатися як функції-члени. Оператори слід перевантажувати лише тоді, коли надана їм поведінка буде природною і однозначною, і вони працюватимуть так як від них очікують. Наприклад, перевантаження оператора + для здійснення операції додавання комплексних чисел це хороше використання цього підходу, в той час перевантаження * для додавання об'єкта у вектор, буде розглядатися як поганий стиль програмування.
- Простий заголовок повідомлення
// приклад перевантаження оператора
#include <string>
class PlMessageHeader
{
std::string m_ThreadSender;
std::string m_ThreadReceiver;
//повертає true, якщо повідомлення однакові, і false в іншому випадку
inline bool operator == (const PlMessageHeader &b) const
{
return ( (b.m_ThreadSender==m_ThreadSender) &&
(b.m_ThreadReceiver==m_ThreadReceiver) );
}
//повертає true якщо повідомлення для імені name
inline bool isFor (const std::string &name) const
{
return (m_ThreadReceiver==name);
}
//повертає true якщо повідомлення для імені name
inline bool isFor (const char *name) const
{
return (m_ThreadReceiver==name);// оскільки тип name це std::string, цей код стає небезпечним, якщо name == NULL
}
};
Використання ключового слова inline в даному прикладі є технічно надмірним, оскільки методи визначені у визначені класу як наведено в прикладі вже неявним чином трактуються як inline.}}
Оператори як функції члени
[ред.]Крім тих операторів, що мають бути членами, оператори можуть перевантажуватися як функції не члени. Цей вибір як здійснювати таке перевантаження залежить від розробника. Оператори перевантажують як члени, коли вони:
- змінюють лівий операнд, або
- потребують прямого доступу до не публічних частин об'єкта.
Коли оператор визначений як член класу, кількість явних параметрів буде зменшена на один, оскільки об'єкт, що в якому здійснюється виклик явним чином є лівим операндом. Таким чином, бінарні оператори приймають один явний параметр, а унарні оператори жодного. У випадку із бінарними операторами, операнд з лівої частини виразу це об'єкт, що здійснює виклик, і ніякого перетворення типів над ним не буде здійснюватися.
// бінарний оператор як функція член класу
//Vector2D Vector2D::operator+(const Vector2D& right)const [...]
// бінарний оператор як функція не член
//Vector2D operator+(const Vector2D& left, const Vector2D& right)[...]
// бінарний оператор як функція не член класу із двома аргументами
//friend Vector2D operator+(const Vector2D& left, const Vector2D& right) [...]
// унарний оператор як функція член класу
//Vector2D Vector2D::operator-()const {...}
// унарний оператор як функція не член класу[...]
//Vector2D operator-(const Vector2D& vec) [...]
Оператори, що можуть бути перевантажені
[ред.]Арифметичні оператори
[ред.]- + (додавання)
- - (віднімання)
- * (множення)
- / (ділення)
- % (модуль)
Як двійкові оператори, наведені вище оператори дозволяють мати аргументи різних типів. Як приклад наведено перевантаження оператора додавання для двовимірного математичного вектору.
Vector2D Vector2D::operator+(const Vector2D& right)
{
Vector2D result;
result.set_x(x() + right.x());
result.set_y(y() + right.y());
return result;
}
Це хороший стиль перевантаження цих операторів, для виконання відповідних користувацьких арифметичних операцій. Оскільки оператор було визначено як функцію-член класу, в ній можна доступитися до прихованих полів класу.