Вы находитесь на странице: 1из 24

Александр Загоруйко © 2020

Operator Overloading
Класс Fraction

Давайте представим, что вы сделали


домашнее задание на класс Fraction 
и есть такой код:
Fraction a(1, 2);
Fraction b(1, 4);
Fraction c = Fraction::Sum(a, b);
c.Print(); // 3/4
Сложение дробей

К сожалению, просто так, по


умолчанию, само по себе, сложение для
нашего типа работать не будет,
поскольку Fraction - это созданный нами
пользовательский тип, и компилятор
понятия не имеет, каким именно
образом, и по каким правилам
необходимо складывать переменные
такого типа…
Перегрузка операций

Однако, в некоторых языках (вроде С++,


C#, Kotlin и тд.) возможность определить
собственные действия для стандартных
операций, применяемых к объектам
пользовательского типа всё-таки
существует, и называется перегрузкой
операторов операций.
Общий синтаксис перегрузки
тип operator знак_операции (параметры)
{
< тело функции >
}
Обычно, когда перегружается унарная
операция, такая функция содержит один
параметр – тот, к которому применяется
операция. Соответственно, когда бинарная –
два параметра.
Первый пример

Пример на перегрузку операции +:


https://git.io/vopCF

Практика:
-перегрузить –
-перегрузить *
-перегрузить /
Разгадка «колдовства»

Fraction result = a + b;

Fraction r = operator + (a, b);


Перегрузка логических операц.
class Fraction
{
double GetDecimal() const
{
return (double)chis / znam;
}
};

bool operator < (const Fraction& f1, const Fraction& f2)


{
return f1.GetDecimal() < f2.GetDecimal();
}
Практика

Перегрузить <и>
Перегрузить <= и >=
Перегрузить == и !=
Перегрузка унарных операций

Fraction operator - (const Fraction& f)


{
Fraction tmp;
tmp.SetChis(-f.GetChis());
tmp.SetZnam(f.GetZnam());
return tmp;
}
Как это работает

Fraction result = -a;

Fraction r = operator -(a);


Перегрузка методом класса
Перегружать операции можно не только
в виде глобальных функций, но и в виде
методов класса (других способов нет :)).
Синтаксис:

тип имя_класса::operator знак_операции (параметры)


{
< тело метода >
}
Особенности перегрузки
В случае оформления перегрузки как метода
класса аналогичным образом происходит замена
операции на вызов метода. Однако, здесь следует
обратить внимание, что при этом в случае
перегрузки унарной операции такой метода не
должен содержать параметров, а в случае
бинарной операции – только один параметр. Так
должно быть потому, что в случае оформления
перегрузки как метода класса в качестве первого
операнда в операции используется сам объект, для
которого вызывается этот метод.
Перегрузка методом класса

Пример перегрузки + в виде метода


класса: https://git.io/vop4A

Практика:
-Перегрузить –, *, /
-Перегрузить операции сравнения
-Перегрузить унарный –
Выбор способа перегрузки
Какой из двух вариантов оформления
перегрузки все же предпочтителен – в виде
функции или в виде метода? С одной стороны,
существуют операции (=, [], (), ->, new/new[],
delete/delete[], преобразование типов),
которые можно перегрузить только как методы
класса, а с другой стороны, подход должен
быть таким: если операция в своей работе
просто создаёт и возвращает новое значение
или объект, то её желательно вынести из
класса.
Практика

 Сделать перегрузку + в виде


глобальной функции
 Сделать перегрузку + в виде метода
класса
 Вызвать a + b
 Проверить, какой вариант перегрузки
выберет компилятор
Перегрузка +=
a += b += c;
Компилятор воспринимает такую строку кода, как:
(a += (b += c));

То есть, он рассматривает эту строку как две


последовательные операции. Для того, чтобы
правильно отработала вторая операция (внешние
скобки), необходимо, чтобы первая операция
(внутренние скобки) в качестве результата
возвращала именно тот же объект, к которому она
применяется.
Код перегрузки +=

Fraction& operator += (const Fraction& f)


{
chis = chis * f.znam + f.chis * znam;
znam *= f.znam;
return *this;
}
Запрещено перегружать

.
::
?:
sizeof
Ограничения перегрузки

 Нельзя перегружать операции для


стандартных типов. Хотя бы одним
операндом в перегружаемой операции
должен быть наш собственный тип.
Т.е. нельзя перегрузить операцию
сложения для двух значений типа int,
но можно, скажем, для значения типа
int и объекта класса Fraction
Ограничения перегрузки

 Нельзя создавать новые названия


операций. Перегружать можно только
уже существующие в С++ операции.
Именно поэтому используется термин
«перегрузка». Т.е. нельзя, скажем,
создать операцию возведения в
степень **, так как такой операции не
существует в языке C++
Ограничения перегрузки

 Перегрузка не меняет приоритет


операций, только само действие
 В С++ нет неявной перегрузки
операций. Т.е. из того, что
перегружены операции + и =, не
следует автоматическая перегрузка
операции +=
Причины использования

 Важно запомнить, что перегрузка


операторов операций применяется
лишь в тех случаях, когда это уместно
 Перегрузка пригодится в реализации
шаблонных (обобщённых) алгоритмов
и структур хранения данных
Реализовать перегрузку

 Fraction + int
 int + Fraction
 Fraction += int
 Fraction == double