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

Матеріал з Вікіпедії — вільної енциклопедії.
Перейти до: навігація, пошук

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

Вступ[ред.ред. код]

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

x+y*z

є зрозумілішим, ніж фраза

помножити 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
}

Критика[ред.ред. код]

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

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

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

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

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

Див. також[ред.ред. код]

Джерела[ред.ред. код]