C++/Об'єктно-орієнтовне програмування/Перевантаження операторів

< 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.}}

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

ред.

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

  1. змінюють лівий операнд, або
  2. потребують прямого доступу до не публічних частин об'єкта.

Коли оператор визначений як член класу, кількість явних параметрів буде зменшена на один, оскільки об'єкт, що в якому здійснюється виклик явним чином є лівим операндом. Таким чином, бінарні оператори приймають один явний параметр, а унарні оператори жодного. У випадку із бінарними операторами, операнд з лівої частини виразу це об'єкт, що здійснює виклик, і ніякого перетворення типів над ним не буде здійснюватися.

    // бінарний оператор як функція член класу
    //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;
}

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