Перейти до вмісту

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

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