История вопроса:
C++ изначально проектировался с акцентом на производительность, поэтому управление ресурсами (память, файлы, потоки, сокеты) чаще происходит вручную. При возникновении исключения требуется очищать захваченные ресурсы. Stack unwinding (разматывание стека) — механизм, используемый C++ для корректного завершения работы функций во время выброса исключения.
Проблема:
При выбрасывании исключения управление немедленно переходит к блокам catch, а промежуточные функции "разматываются": их деструкторы вызываются, но явные вызовы освобождающих функций могут быть пропущены (например, если не используются объекты, автоматически освобождающие ресурсы).
Решение:
В C++ освобождение ресурсов следует поручать деструкторам, а не вызывать освобождающие функции вручную. Шаблон RAII (Resource Acquisition Is Initialization) — однозначный способ сделать освобождение ресурса автоматическим. При stack unwinding вызовется деструктор, и ресурс освободится независимо от пути выхода из функции.
Пример кода:
#include <fstream> #include <stdexcept> void readFile(const std::string& filename) { std::ifstream file(filename); // Откроется и корректно закроется даже при исключении if (!file.is_open()) { throw std::runtime_error("File cannot be opened"); } // ... читаем файл } // file закроется даже при исключении
Ключевые особенности:
Если в коде есть delete ptr; в блоке catch, этого достаточно для очистки памяти?
Нет, если между выделением памяти и блоком catch появится исключение, память может не быть очищена. Лучше использовать std::unique_ptr или писать delete в деструкторе.
Пример кода:
void foo() { int* data = new int[10]; // ... throw std::runtime_error("fail"); delete[] data; // не вызовется при исключении }
Может ли stack unwinding пропустить вызов деструктора для объекта, размещённого на стеке?
Нет, все локальные объекты (не уничтоженные до точки выброса исключения) будут уничтожены в обратном порядке создания, деструкторы вызовутся гарантированно.
Можно ли использовать goto или longjmp для выхода из try-блока и рассчитывать на вызов деструкторов?
Нет. C++ гарантирует вызов деструкторов только при stack unwinding из-за исключения, а не из-за некорректного управления потоком (goto, setjmp/longjmp).
Программист выделяет память с помощью new, обрабатывает исключения, освобождая память в блоке catch, но забывает о других путях выхода из функции.
Плюсы:
Минусы:
Используется std::unique_ptr и RAII-классы для всех ресурсов, освобождение не зависит от try/catch.
Плюсы:
Минусы: