Назад к вопросам
Middle+
77
questionbank
В чем заключается особенность выброса исключений в конструкторах и деструкторах?
Sobes Copilot
Получайте ответы в реальном времени
Ответ от нейросети
sobes.tech AI
Выброс исключения в конструкторе:
- Если исключение выбрасывается в конструкторе, деструктор объекта не вызывается.
- Необходимо позаботиться об освобождении уже выделенных ресурсов до момента выброса или использовать идиому RAII (Resource Acquisition Is Initialization) для управления ресурсами.
- Частично сконструированный объект находится в неопределенном состоянии.
Выброс исключения в деструкторе:
- Выброс исключения из деструктора в процессе раскрутки стека (stack unwinding) из-за другого исключения приводит к вызову
std::terminate, которое прерывает выполнение программы. Это очень плохо. - Деструкторы должны быть
noexcept. - Если в деструкторе необходимо выполнить операцию, которая может выбросить исключение (например, закрытие файла), следует обработать это исключение внутри деструктора (например, записать в лог) вместо его дальнейшего распространения.
#include <iostream>
#include <fstream>
class ResourceHolder {
std::ofstream file;
bool opened = false;
public:
ResourceHolder(const std::string& filename) {
file.open(filename);
if (!file.is_open()) {
// Выброс исключения в конструкторе
throw std::runtime_error("Failed to open file: " + filename);
}
opened = true;
std::cout << "Resource acquired successfully." << std::endl;
}
~ResourceHolder() noexcept { // Деструктор noexcept
if (opened) {
try {
file.close();
std::cout << "Resource released successfully." << std::endl;
} catch (const std::exception& e) {
// Обработка исключения внутри деструктора
std::cerr << "Exception caught in destructor: " << e.what() << std::endl;
// НЕ выбрасываем исключение дальше
}
} else {
std::cout << "Resource release skipped (not opened)." << std::endl;
}
}
};
int main() {
try {
// Пример выброса в конструкторе
// ResourceHolder res("non_existent_file.txt");
// Пример успешного конструирования и освобождения
ResourceHolder res("test.txt");
} catch (const std::runtime_error& e) {
std::cerr << "Caught exception: " << e.what() << std::endl;
}
// Пример некорректного деструктора (для иллюстрации, НЕ ПОВТОРЯЙТЕ)
/*
class BadDestructor {
int* data;
public:
BadDestructor() : data(new int(10)) {}
~BadDestructor() {
delete data;
// Симулируем выброс исключения в деструкторе (НЕ ДЕЛАЙТЕ ТАК)
// throw std::runtime_error("Exception from bad destructor");
}
};
try {
BadDestructor bad_obj;
throw std::runtime_error("Another exception"); // Вызываем другое исключение
} catch (...) {
// При наличии исключения в деструкторе при раскрутке стека
// будет вызван std::terminate
std::cerr << "Caught some exception in main." << std::endl;
}
*/
return 0;
}
| Особенность | Конструктор | Деструктор |
|---|---|---|
| Вызов деструктора | Не вызывается, если выброшено исключение | Обычно вызывается (при успешном конструировании) |
| Неопределенное состояние | Частично сконструированный объект | - |
| RAII | Важно для управления ресурсами | - |
noexcept |
Необязательно, но полезно знать о возможности ошибки | Крайне рекомендовано быть noexcept |
| Раскрутка стека | Участвует в процессе | Выброс исключения во время раскрутки стека ведет к std::terminate |