Назад к вопросам
Middle+
91
questionbank
Что может пойти не так, если выбросить исключение из конструктора?
Sobes Copilot
Получайте ответы в реальном времени
Ответ от нейросети
sobes.tech AI
- Частично построенный объект. Если исключение выбрасывается во время создания объекта, деструктор для этого объекта не будет вызван автоматически, что может привести к утечке ресурсов, выделенных до момента выброса исключения.
- Неопределенное состояние объекта. Объект может находиться в состоянии, когда часть его инициализации выполнена, а часть нет, что затрудняет дальнейшую работу с ним или его корректное удаление.
- Утечка ресурсов. Если конструктор выделяет ресурсы (память, файловые дескрипторы и т.д.) до момента исключения, эти ресурсы могут остаться незавершенными и привести к утечкам.
- Проблемы с массивами объектов. При создании массива объектов, если конструктор одного из элементов массива выбрасывает исключение, деструкторы для уже успешно созданных элементов массива не будут вызваны, что также ведет к утечкам ресурсов.
- Неопределённое поведение при использовании
new. Еслиnewне может выделить память или конструктор объекта выбрасывает исключение,newвыбрасываетstd::bad_allocили исключение конструктора соответственно. Важно правильно обрабатывать такие случаи.
Рекомендуется избегать выброса исключений из конструкторов. В качестве альтернативы можно использовать следующие подходы:
- Использовать функцию-член
init(), которая вызывается после успешного конструирования объекта и может выбрасывать исключения. - Использовать паттерн "Resource Acquisition Is Initialization" (RAII), где управление ресурсами связано с временем жизни объекта. Специализированные классы-обертки (например, интеллектуальные указатели, RAII-классы для файлов) гарантируют освобождение ресурса при выходе из области видимости, даже при наличии исключений.
#include <iostream>
#include <string>
#include <vector>
class Resource {
public:
Resource(const std::string& name) : name_(name) {
std::cout << "Resource '" << name_ << "' acquired." << std::endl;
}
~Resource() {
std::cout << "Resource '" << name_ << "' released." << std::endl;
}
private:
std::string name_;
};
class MyClass {
public:
// Некорректный конструктор, выбрасывающий исключение
MyClass() : res1_("Res1") {
std::cout << "MyClass constructor part 1..." << std::endl;
if (true) { // Имитация ошибки
throw std::runtime_error("Error during MyClass construction");
}
// Эта часть не будет выполнена, Res2 не будет создан
res2_ = Resource("Res2");
std::cout << "MyClass constructor part 2..." << std::endl;
}
~MyClass() {
std::cout << "MyClass destructor called." << std::endl;
// Деструктор для res1_ будет вызван автоматически
// Но res2_ не был создан, его деструктор не будет вызван
}
private:
Resource res1_;
Resource res2_; // Проблема: если исключение выбрасывается до инициализации res2_
};
// Пример альтернативы с init() функцией
class SafeMyClass {
public:
SafeMyClass() {
std::cout << "SafeMyClass constructor called." << std::endl;
}
~SafeMyClass() {
std::cout << "SafeMyClass destructor called." << std::endl;
}
void init() {
std::cout << "SafeMyClass init() part 1..." << std::endl;
res1_ = Resource("SafeRes1"); // Инициализация члена после конструирования
if (true) { // Имитация ошибки
throw std::runtime_error("Error during SafeMyClass initialization");
}
res2_ = Resource("SafeRes2");
std::cout << "SafeMyClass init() part 2..." << std::endl;
}
private:
Resource res1_;
Resource res2_;
};
int main() {
// Пример использования некорректного конструктора
try {
MyClass* obj = new MyClass(); // Проблема: если конструктор падает, obj указывает на мусор
delete obj; // Может не произойти, если исключение в конструкторе
} catch (const std::exception& e) {
std::cerr << "Caught exception: " << e.what() << std::endl;
// Ресурс Res1 будет освобожден, но если была утечка до него, она останется
}
std::cout << "---" << std::endl;
// Пример использования SafeMyClass
try {
SafeMyClass obj; // Конструктор не выбрасывает
obj.init(); // Инициализация и потенциальное исключение здесь
} catch (const std::exception& e) {
std::cerr << "Caught exception: " << e.what() << std::endl;
// Деструктор SafeMyClass будет вызван (для объекта obj)
// Деструктор res1_ будет вызван корректно, так как он был создан
// res2_ не был создан, утечки здесь нет
}
return 0;
}