Произойдет ли утечка памяти при использовании `new`, если в конструкторе объекта выбрасывается исключение?

«Произойдет ли утечка памяти при использовании `new`, если в конструкторе объекта выбрасывается исключение?» — вопрос из категории Управление памятью, который задают на 25% собеседований C/C++ Разработчик. Ниже — развёрнутый ответ с разбором ключевых моментов.

Ответ

Да, произойдет. Если исключение выбрасывается в теле конструктора после успешного выполнения new, выделенная память не будет освобождена автоматически. new в C++ выполняет два шага:

  1. Выделяет память (operator new).
  2. Вызывает конструктор.

Если конструктор завершается исключением, память, выделенная на шаге 1, не освобождается, так как полноценный объект не был создан и деструктор для него не будет вызван.

Пример утечки:

class Widget {
    int* data;
public:
    Widget(size_t size) {
        data = new int[size]; // (1) Выделили память
        // ... какая-то логика ...
        if (some_condition) {
            throw std::runtime_error("Construction failed"); // (2) Исключение!
        }
        // Память, выделенная для `data`, утекла.
    }
    ~Widget() { delete[] data; }
};

void foo() {
    try {
        Widget* w = new Widget(100); // Утечка при исключении в конструкторе
    } catch (...) { /* Память для w->data уже потеряна */ }
}

Решение: Использовать умные указатели (std::unique_ptr, std::shared_ptr) или технику RAII для управления сырыми ресурсами внутри класса до того, как может быть выброшено исключение.

Исправленный вариант:

#include <memory>

class SafeWidget {
    std::unique_ptr<int[]> data; // RAII-обертка
public:
    SafeWidget(size_t size) : data(std::make_unique<int[]>(size)) {
        // Память уже управляется unique_ptr.
        if (some_condition) {
            throw std::runtime_error("Failed");
        }
        // При выбросе исключения деструктор unique_ptr освободит память.
    }
    // Деструктор по умолчанию корректен.
};