О чем стоит помнить, когда выбрасывается исключение из конструктора?
Sobes Copilot
Получайте ответы в реальном времени
Ответ от нейросети
sobes.tech AI
При выбросе исключения из конструктора необходимо помнить о неполной инициализации объекта.
Автоматически высвобождаются ресурсы, полученные в конструкторе до точки выброса исключения (локальные переменные, члены класса с автоматическим временем жизни). Ресурсы, выделенные динамически (например, через new), но не обернутые в умные указатели, не будут автоматически освобождены, что приведет к утечке памяти.
Пример утечки памяти:
#include <iostream>
#include <stdexcept>
class MyClass {
public:
MyClass() : data1_(nullptr), data2_(nullptr) {
data1_ = new int; // Память выделена
if (!data1_) throw std::runtime_error("Failed to allocate data1");
data2_ = new int; // Память выделена
// Предположим, здесь что-то идет не так и выбрасывается исключение
if (true /* условие ошибки */) {
// data1_ не будет удален
throw std::runtime_error("Error during initialization");
}
}
~MyClass() {
delete data1_;
delete data2_;
}
private:
int* data1_;
int* data2_;
};
int main() {
try {
MyClass obj;
} catch (const std::exception& e) {
std::cerr << "Exception caught: " << e.what() << std::endl;
}
// Возможна утечка памяти data1_
return 0;
}
Пример с использованием умного указателя для предотвращения утечки:
#include <iostream>
#include <stdexcept>
#include <memory> // Для std::unique_ptr
class MyClass {
public:
MyClass() : data1_(nullptr), data2_(nullptr) {
data1_ = std::make_unique<int>(); // Память управляется unique_ptr
// Предположим, здесь что-то идет не так и выбрасывается исключение
if (true /* условие ошибки */) {
// data1_ будет автоматически удален при выходе из области видимости
throw std::runtime_error("Error during initialization");
}
data2_ = std::make_unique<int>();
}
// Деструктор не требуется для уникальных указателей (RAII)
// ~MyClass() = default;
private:
std::unique_ptr<int> data1_;
std::unique_ptr<int> data2_;
};
int main() {
try {
MyClass obj;
} catch (const std::exception& e) {
std::cerr << "Exception caught: " << e.what() << std::endl;
}
// Утечки памяти нет
return 0;
}
Важно использовать принцип Resource Acquisition Is Initialization (RAII), оборачивая управляемые ресурсы в объекты, деструкторы которых гарантируют их освобождение при выходе из области видимости или при уничтожении объекта. Умные указатели (std::unique_ptr, std::shared_ptr) являются стандартным способом реализации RAII для динамически выделенной памяти. Контейнеры стандартной библиотеки также следуют этому принципу.
Также следует помнить, что деструктор объекта, из конструктора которого было выброшено исключение, не вызывается.
#include <iostream>
#include <stdexcept>
class MyClass {
public:
MyClass() {
std::cout << "Constructor called" << std::endl;
throw std::runtime_error("Error in constructor");
}
~MyClass() {
// Этот деструктор не будет вызван, если конструктор выбросил исключение
std::cout << "Destructor called" << std::endl;
}
};
int main() {
try {
MyClass obj;
} catch (const std::exception& e) {
std::cerr << "Exception caught: " << e.what() << std::endl;
}
return 0;
}