Назад к вопросам
Middle+
73
questionbank

О чем стоит помнить, когда выбрасывается исключение из конструктора?

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;
}