Перевантаження операторів

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

Вступ

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

x+y*z

є зрозумілішим[lower-alpha 1], ніж фраза

помножити y на z і додати результат до x

або навіть

add (x, multiply (y,z))

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

Приклад

#include <stdio.h> // для printf

class X 
{
    int x;
public:
    X() : x(0) {}
    explicit X(int i) : x(i) {}
    operator int() { return x; }
    X& operator=(const X& arg1) { x = arg1.x; return *this; }
    friend const X operator+(const X&, const X&);
    friend const X operator*(const X&, const X&);
};

// використання дружніх функцій дозволяє доступ до приватних членів класу
// і також дозволяє показати симетричність в оголошенні функції.
// застосування модифікатора const до значення, яке повертається, перешкоджає
// написанню дивних виразів подібних до X(5) + X(6) = X(7)
inline const X operator+(const X& arg1, const X& arg2)
{
    X r;
    r.x = arg1.x + arg2.x;
    return r;
}

inline const X operator*(const X& arg1, const X& arg2)
{
    X r;
    r.x = arg1.x * arg2.x;
    return r;
}

int main()
{
    X x;
    x = X(5) + X(6) * X(7); // = 47 ( * має вищий пріоритет )
                            // x = 5 + 6 * 7; - не спрацює через використання explicit,
                            // а x = X(5 + 6 * 7); можна
    printf("%d\n", x); // завдяки operator int() х неявно перетворюється в int
}

Критика та експертний аналіз явища

Перевантаження операторів часто підпадає під критику через те, що воно дозволяє надавати операторам зовсім різну семантику в залежності від типу їх операндів. Наприклад, використання << в C++:

a << 1

зсуває біти в змінній a ліворуч на 1 біт, якщо a належить до цілочисельного типу, але якщо a це потік виведення, тоді такий код буде намагатися вивести "1" в потік. Через те, що перевантаження операторів дозволяє програмісту, що почав писати код програми, змінити семантику операторів, то інші розробники, що будуть працювати далі на кодом, можуть зіткнутись із неочікуваною поведінкою операторів. Тож вважається доброю практикою з обережністю ставитися до перевантаження операторів.

Поширена відповідь на цю критику програмістів, які надають перевагу перевантаженню операторів, полягає у тому, що те саме можна сказати і про перевантаження функцій. Далі більше — навіть без можливості перевантаження програміст може написати функцію, яка буде робити зовсім не те, що від неї можна очікувати, беручи до уваги її назву. Крім того, такі мови як С++ обмежують набір операторів для перевантаження, тобто не можна визначити нову лексему оператора.

Теорія кілець

Інший, більш тонкий момент з операторами такий, що можуть бути очікувані звичайні математичні правила. Наприклад, комутативність операції + (тобто a + b == b + a) не завжди виконується; наприклад, коли операндами є рядки (тобто результат "свято" + "весна" відрізняється від "весна" + "свято"). Типовим зустрічним аргументом, що випливає з математики, є: допоки + комутативний для цілих чисел (і, загалом, для кожного кільця), він не комутативний для інших типів змінних. Можна також зауважити, що на практиці + навіть не є асоціативним для чисел з рухомою комою зважаючи на проблему заокруглювання.

Оператори перевантажуються таким чином, що їхні об'єкти ведуть себе як примітивні типи.

Нюанси з оптимізацією

Також проблемою з точки зору швидкодії є те, що перевантаження операторів не дає ніякої інформації про зв'язки між ними. Наприклад, у випадку беззнакового цілого x вирази x * 4, x + x + x + x та x << 2 еквівалентні, і компілятор може використовувати цю еквівалентність в цілях оптимізації. З іншого боку, якщо x — це об'єкт деякого гіпотетичного класу Integer, то ці вирази будуть неминуче інтерпретовані буквально. Візьмемо реальний приклад: матричний вираз A * B + A * C має потенціал для оптимізації, який не може бути реалізований через точний порядок виконання, що є результатом перевантаження операторів * та +.

Класифікація

Класифікація деяких мов програмування за можливістю перевантаження і розширення набору операторів.

Множина
операторів
Перевантаження ,
ні
Перевантаження ,
так
Обмежена

Сі
Java
Objective C
Pascal
PHP BASIC

Ada
C++
C#
Object Pascal
Perl
Python
Rust

Можливо
вводити нові

ML
Pico
LISP[джерело?]

Алгол 68
Fortran
Haskell
PostgreSQL
Пролог
Ruby
Perl 6
Smalltalk

Див. також

Примітки

  1. На початку розвитку мов програмування дана думка преваліювала не у всіх інженерних і наукових колах. Яскравим прикладом є мова COBOL, де синтаксис, що нагадує вирази англійською мовою (на зразок MULTIPLY Y BY Z GIVING Q), покладено у саму основу мови.

Джерела

    Література

    This article is issued from Wikipedia. The text is licensed under Creative Commons - Attribution - Sharealike. Additional terms may apply for the media files.