Лекция 8

05.09.2025 Обновлено: 05.09.2025

Лекция 8. Перегрузка функций, методов и операторов

Операторы (классификация)

1. Арифметические:

  • Унарные префиксные: +, -, ++, --
  • Унарные постфиксные: ++, --
  • Бинарные: +, -, *, /, %, +=, -=, *=, /=, %=

2. Битовые:

  • Унарные: ~
  • Бинарные: &, |, ^, &=, ^=, |=, >>, <<, >>=, <<=

3. Логические:

  • Унарные: !
  • Бинарные: &&, ||
  • Сравнения: ==, !=, >, <, >=, <=

Прочие:

  1. Оператор присваивания: =
  2. Специальные:
    • Префиксные: *, &
    • Постфиксные: ->, ->*
    • Особые: ,, ::
  3. Скобки: [], ()
  4. Оператор приведения: (type)
  5. Тернарный оператор: x ? y : z
  6. Работа с памятью: 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, а в classprivate.
  • Идейно: struct — просто набор данных (методы обычно не определяются), а class имеет кучу идиом и принципов ООП.