ПрограммированиеC++ разработчик, системный программист

Расскажите о механизме operator new/operator delete в C++. Как они отличаются от new/delete, когда и зачем операторные функции переопределяются в пользовательских классах?

Проходите собеседования с ИИ помощником Hintsage

Ответ.

В C++ operator new и operator delete — это функции выделения и освобождения памяти, которые вызываются при размещении и удалении объектов через операторы new и delete. По умолчанию они используют стандартный аллокатор, но в классе их можно перегрузить для тонкого контроля выделения памяти.

  • operator new выделяет "сырой" участок памяти, не вызывая конструктор.
  • После выделения памяти автоматически вызывается конструктор объекта.
  • operator delete освобождает память, после завершения деструктора объекта.

Использование (глобальные и локальные версии):

  • Глобальная версия применяется по-умолчанию (например, при new int).
  • Переопределяя в классе специфичный operator new, можно оптимизировать работу с памятью для объектов этого класса (например, пул объектов, трассировка, переиспользование блоков).

Пример перегрузки operator new/operator delete:

#include <iostream> class TrackAlloc { public: void* operator new(size_t size) { std::cout << "TrackAlloc::new for " << size << " bytes "; return ::operator new(size); } void operator delete(void* ptr) { std::cout << "TrackAlloc::delete "; ::operator delete(ptr); } };

Тонкости:

  • Вызов новых/удаления объектов с аргументами (placement new) требует отдельной перегрузки.
  • Переопределённые операторы вызываются только при создании/удалении объектов класса, а не для new[]/delete[]. Для них можно перегрузить отдельные operator new[]/delete[].
  • Манипуляции с памятью опасны — требуется корректная обработка исключений.

Вопрос с подвохом.

"Что произойдёт, если переопределить оператор new в классе, а затем создать объект через переменную-унаследованного класса? Какая версия operator new вызовется?"

Ответ: Будет вызван operator new класса, от имени которого создаётся объект. Если у производного класса не реализован operator new, будет попытка найти подходящую версию у базового класса или глобальную версию.

Пример:

struct Base { void* operator new(size_t s) { std::cout << "Base new "; return ::operator new(s); } }; struct Derived : Base {}; Derived* p = new Derived; // Вызовет Base::operator new!

Примеры реальных ошибок из-за незнания тонкостей темы.


История

Разработчики перегрузили operator new/ delete без поддержки корректной обработки исключений. При выбрасывании исключения внутри конструктора память не освобождалась, что приводило к утечкам.


История

Некорректно реализовали operator new[] и operator delete[]: для класса, внутри которого сдержались массивы, новая реализация не вызывалась — использовались дефолтные версии, что приводило к рассинхронизации логики выделения и освобождения памяти.


История

Переопределение глобального operator new повлияло на работу сторонних библиотек: все объекты (в том числе временные и из STL) начали аллоцироваться через логгируемый аллокатор, что критически замедлило работу ядра приложения.