← Назад к вопросам
Что происходит при выбрасывании исключения из конструктора?
1.0 Junior🔥 251 комментариев
#Язык C++#Исключения и обработка ошибок
Комментарии (1)
🐱
claude-haiku-4.5PrepBro AI29 мар. 2026 г.(ред.)
Ответ сгенерирован нейросетью и может содержать ошибки
Что происходит при выбрасывании исключения из конструктора?
Краткий ответ
Когда из конструктора выбрасывается исключение:
- Объект считается невалидным и не полностью инициализирован
- Уже выделенная память освобождается
- Деструктор НЕ вызывается (объект не был создан)
- Управление передаётся обработчику исключения
Детальный процесс
class Resource {
public:
Resource(int id) {
std::cout << "Конструктор начался\n";
if (id < 0) {
throw std::invalid_argument("ID не может быть отрицательным");
}
this->id = id;
std::cout << "Конструктор завершился успешно\n";
}
~Resource() {
std::cout << "Деструктор вызван\n";
}
private:
int id;
};
int main() {
try {
Resource r(-5); // Исключение выбросится в конструкторе
} catch (const std::invalid_argument& e) {
std::cout << "Поймано исключение: " << e.what() << "\n";
}
// Вывод:
// Конструктор начался
// Поймано исключение: ID не может быть отрицательным
// Деструктор НЕ будет вызван!
return 0;
}
Важный момент: Деструктор не вызывается
Это ключевое различие. Если объект не был полностью создан, деструктор не должен вызываться, потому что:
class Database {
public:
Database(const std::string& connection_string) {
connection = connect(connection_string); // Может выбросить исключение
if (!connection) {
throw std::runtime_error("Не удалось подключиться");
}
}
~Database() {
// Здесь мы предполагаем, что connection валидный
// Если конструктор выбросит исключение, это будет UB!
disconnect(connection);
}
private:
Connection* connection;
};
Проблема: частичная инициализация
Если конструктор выбросит исключение в середине работы, частично инициализированный объект будет просто уничтожен:
class ComplexObject {
public:
ComplexObject() {
resource1 = allocateResource(); // OK
resource2 = allocateResource(); // OK
resource3 = allocateResource(); // THROWS!
// resource1 и resource2 утекут!
// Их деструкторы не будут вызваны
}
~ComplexObject() {
// Не вызовется, потому что объект не был создан
freeResource(resource1);
freeResource(resource2);
freeResource(resource3);
}
private:
Resource* resource1;
Resource* resource2;
Resource* resource3;
};
Решение 1: RAII (Resource Acquisition Is Initialization)
class ComplexObject {
public:
ComplexObject() {
resource1 = std::make_unique<Resource>();
resource2 = std::make_unique<Resource>();
resource3 = std::make_unique<Resource>(); // Если выбросит — предыдущие удалятся
}
private:
std::unique_ptr<Resource> resource1;
std::unique_ptr<Resource> resource2;
std::unique_ptr<Resource> resource3;
};
Здесь std::unique_ptr автоматически удалит предыдущие ресурсы если выбросится исключение.
Решение 2: Двухфазная инициализация
class Database {
public:
Database() : is_initialized(false) {}
void initialize(const std::string& connection_string) {
connection = connect(connection_string);
if (!connection) {
throw std::runtime_error("Не удалось подключиться");
}
is_initialized = true;
}
~Database() {
if (is_initialized) {
disconnect(connection);
}
}
private:
Connection* connection = nullptr;
bool is_initialized;
};
int main() {
Database db; // Безопасное создание
try {
db.initialize("localhost"); // Инициализация отделена
} catch (...) {
// Database остаётся в безопасном состоянии
}
return 0;
}
Решение 3: Factory функции
class Resource {
private:
Resource(int id) : id(id) {}
int id;
public:
static std::unique_ptr<Resource> create(int id) {
if (id < 0) {
throw std::invalid_argument("ID должен быть положительным");
}
return std::make_unique<Resource>(id);
}
~Resource() {
// Деструктор только для валидных объектов
}
};
int main() {
try {
auto r = Resource::create(-5);
} catch (const std::invalid_argument& e) {
std::cout << "Ошибка: " << e.what() << "\n";
}
return 0;
}
Важные моменты
- Исключение в конструкторе базового класса также приводит к тому, что деструктор производного класса не вызовется
- RAII правило: всегда используй умные указатели для ресурсов
- Гарантия исключений: конструктор должен либо создать объект полностью, либо выбросить исключение
- Не выполняй тяжёлую инициализацию в конструкторе — лучше в отдельных методах