Лекция 8. Перегрузка функций, методов и операторов
Операторы (классификация)
1. Арифметические:
- Унарные префиксные:
+,-,++,-- - Унарные постфиксные:
++,-- - Бинарные:
+,-,*,/,%,+=,-=,*=,/=,%=
2. Битовые:
- Унарные:
~ - Бинарные:
&,|,^,&=,^=,|=,>>,<<,>>=,<<=
3. Логические:
- Унарные:
! - Бинарные:
&&,|| - Сравнения:
==,!=,>,<,>=,<=
Прочие:
- Оператор присваивания:
= - Специальные:
- Префиксные:
*,& - Постфиксные:
->,->* - Особые:
,,::
- Префиксные:
- Скобки:
[],() - Оператор приведения:
(type) - Тернарный оператор:
x ? y : z - Работа с памятью:
new,new[],delete,delete[]
Перегрузка операторов — правила
- Не должна противоречить здравой логике (
+должен делать что-то похожее на сложение). - Может быть членом класса или глобальной функцией.
[],(),->,=— всегда члены класса.- Ввод (
>>) и вывод (<<) — всегда глобальные функции. - Операторы
::,.*,.,?:— перегружать нельзя. - Новые операторы создать нельзя.
Способы перегрузки
| Оператор | Как член класса | Не как член класса |
|---|---|---|
@a | (a).operator@() | operator@(a) |
a@b | (a).operator@(b) | operator@(a, b) |
a=b | (a).operator=(b) | — |
a(b...) | (a).operator()(b...) | — |
a[b] | (a).operator[](b) | — |
a-> | (a).operator->() | — |
a@ (постфикс) | (a).operator@(0) | operator@(a, 0) |
Операторы ввода / вывода
operator<<иoperator>>.- Не как члены класса (если бы были — поток был бы типом, к которому применяется оператор, т.к. первый аргумент — это объект, к которому применяется метод).
std::ostream& operator<<(std::ostream& stream, const Type& value);
std::istream& operator>>(std::istream& stream, Type& value);
Ключевое слово friend
Иногда называют «костылём, который ломает инкапсуляцию».
- Дружественные функции — это функции, которые не являются членами класса, однако имеют доступ к его закрытым членам (
privateиprotected). - Определение этих функций производится вне класса.
- Дружественные функции могут определяться в другом классе.
- Если нужно много функций из другого класса — можно целый класс объявить дружественным (
friend class Foo;).
Functor (функтор) / Function object
- Объект функции — объект, который может вызываться как функция.
- Для этого применяется
operator()(его можно перегрузить).
struct Adder {
int x;
int operator()(int y) const { return x + y; }
};
Adder add5{5};
add5(10); // 15
- Имеет смысл, когда в функции должно отражаться какое-то состояние (которое хранится в полях функтора).
RAII (Resource Acquisition Is Initialization)
- Захват ресурса неразрывно совмещается с инициализацией объекта, а освобождение — с уничтожением объекта.
- В основе RAII лежит идея связывания жизненного цикла ресурса (памяти, файлового дескриптора и т.п.) с жизненным циклом объекта в C++.
- Это означает, что ресурсы выделяются и освобождаются автоматически при создании и уничтожении объектов.
Преимущества:
- Автоматическое управление ресурсами.
- Безопасность (включая при исключениях).
- Повышение читаемости кода.
- Повышение производительности.
- Поддержка стандартных контейнеров.
Когда объект RAII создаётся, он гарантирует, что ресурсы будут правильно инициализированы. Когда объект RAII выходит из области видимости, его деструктор гарантирует, что ресурсы будут корректно освобождены, даже в случае исключения.
Принципы:
- Тот, кто ресурс создал — тот этим ресурсом и владеет!
- Если ресурс нужно передать — делаем это явно! (хвост передачи владения)
Операторы разыменования и доступа
Foo& operator*();
Foo* operator->();
Часто используются в умных указателях и итераторах.
Префиксный vs постфиксный инкремент
- Префиксный (
++x) — просто меняет данный объект, возвращает ссылку на себя. Дешевле. - Постфиксный (
x++) — сначала делает копию, возвращает копию. Работает дольше. - Для постфиксного нужен фиктивный
intв аргументе (передавать не обязательно):
MyClass& operator++(); // префиксный
MyClass operator++(int); // постфиксный
Приведение типов
operator Type() const; // оператор приведения к типу Type
Проблема malloc
mallocне вызывает конструктор объекта, поэтому не соблюдается инвариант.newрулит! (соответственно иdeleteпротивfree;reallocтоже не зовёт конструкторы).
Class vs Structure
- По синтаксису почти ничем не отличаются (даже наследование работает одинаково).
- По умолчанию в
structвсёpublic, а вclass—private. - Идейно:
struct— просто набор данных (методы обычно не определяются), аclassимеет кучу идиом и принципов ООП.