Будет ли вызван деструктор для объекта, конструктор которого бросил исключение?
Комментарии (1)
Ответ сгенерирован нейросетью и может содержать ошибки
Будет ли вызван деструктор при исключении в конструкторе?
Краткий ответ: Нет, деструктор объекта НЕ будет вызван, если его конструктор бросил исключение. Однако деструкторы УЖЕ инициализированных членов класса вызваны БУДУТ.
Почему деструктор самого объекта не вызывается?
Это ключевое правило в C++: если конструктор завершился с исключением, объект считается не полностью инициализированным, а поэтому он не существует. Раз объекта нет, деструктор вызывать нечему.
class Resource {
public:
Resource(int id) {
std::cout << "Конструктор вызван\n";
if (id < 0) {
throw std::invalid_argument("ID не может быть отрицательным");
}
}
~Resource() {
std::cout << "Деструктор вызван\n";
}
};
int main() {
try {
Resource r(-1); // Конструктор бросит исключение
// Деструктор НЕ будет вызван
} catch (const std::exception& e) {
std::cout << "Перехвачено: " << e.what() << "\n";
}
}
Вывод: только строка "Конструктор вызван", без "Деструктор вызван".
А что с членами класса?
Здесь ситуация сложнее. Если конструктор инициализировал членов класса перед тем, как бросить исключение, деструкторы этих членов БУДУТ вызваны в обратном порядке инициализации:
class Logger {
public:
Logger(const std::string& name) {
std::cout << "Logger: " << name << " создан\n";
}
~Logger() {
std::cout << "Logger уничтожен\n";
}
};
class Container {
public:
Logger log1{"log1"};
Logger log2{"log2"};
Container() {
std::cout << "Container конструктор начался\n";
throw std::runtime_error("Ошибка в конструкторе");
}
~Container() {
std::cout << "Container деструктор\n";
}
};
int main() {
try {
Container c;
} catch (...) {}
}
Вывод:
Logger: log1 создан
Logger: log2 создан
Container конструктор начался
Logger уничтожен // log2 в обратном порядке
Logger уничтожен // log1 в обратном порядке
Деструктор самого Container НЕ вызван, но деструкторы его членов — ДА.
Практические следствия
-
RAII паттерн: используй членские переменные типов, у которых есть деструкторы (std::string, std::vector, std::unique_ptr). Они очистят ресурсы автоматически.
-
Инициализация и исключения: если конструктор должен быть безопасным, инициализируй члены в правильном порядке и бросай исключения рано.
class SafeResource {
std::unique_ptr<int> ptr;
std::vector<int> data;
public:
SafeResource(int size)
: ptr(std::make_unique<int>(size)), data(size) {
// Если бросим исключение здесь,
// ptr и data очищены автоматически
if (size > 1000000) {
throw std::out_of_range("Слишком большой размер");
}
}
};
- Function-try-block: в исключительных случаях можно перехватить исключение прямо в конструкторе:
MyClass::MyClass() try
: member1(), member2() {
// код конструктора
} catch (const std::exception& e) {
// обработка, но нужно пробросить исключение дальше
throw; // иначе объект считается инициализированным!
}
Вывод
Деструктор объекта, конструктор которого бросил исключение, не вызывается. Но это не проблема, если ты используешь RAII и умные указатели — они позаботятся об очистке ресурсов за счёт деструкторов своих членов.