Освобождается ли память, когда я выкидываю исключение? - PullRequest
7 голосов
/ 20 августа 2009

Я спорил с некоторыми коллегами о том, что происходит, когда вы генерируете исключение в динамически распределенном классе. Я знаю, что вызывается malloc, а затем конструктор класса. Конструктор никогда не возвращается, так что же происходит с malloc?

Рассмотрим следующий пример:

class B
{
public:
    B()
    {
        cout << "B::B()" << endl;
        throw "B::exception";
    }

    ~B()
    {
        cout << "B::~B()" << endl;          
    }
};

void main()
{
    B *o = 0;
    try
    {
        o = new B;
    }
    catch(const char *)
    {
        cout << "ouch!" << endl;
    }
}

Что происходит с ошибочной памятью o, течет ли она? CRT перехватывает исключение конструктора и освобождает память?

Ура!
Рич

Ответы [ 4 ]

10 голосов
/ 20 августа 2009

Звонок на

new B();

разрешается в двух вещах:

  • выделение с помощью оператора new () (либо глобального, либо специфичного для класса, возможно, размещения с синтаксисом new (xxx) B())
  • вызов конструктора.

Если конструктор throw, вызывается соответствующий оператор delete. Случай, когда соответствующее удаление является местом размещения, является единственным случаем, когда оператор удаления места размещения вызывается без синтаксиса :: оператор delete (). delete x; или delete[] x; не вызывают операторы удаления мест размещения, и нет аналогичного синтаксиса для размещения новых, чтобы вызывать их.

Обратите внимание, что хотя деструктор B не будет вызываться , а не , уже построенные подобъекты (члены или B и базовые классы B) будут разрушены до вызова оператора delete. Конструктор, который не вызывается, предназначен для B.

6 голосов
/ 20 августа 2009

Когда из конструктора выдается исключение, память, выделенная new, освобождается, но деструктор класса B. не вызывается.

2 голосов
/ 20 августа 2009

В этом случае ваш объект o фактически не создается, и память, выделенная новым, освобождается. Таким образом, деструктор не вызывается. Так что вам НЕ нужно звонить:

delete o;

Интересный шаблон проектирования - RAII - Инициализация ресурсов - это инициализация. В этом шаблоне вы используете конструктор для инкапсуляции получения ресурса и освобождаете ресурс в деструкторе. Если ресурс не может быть получен, вы добавляете конструктор - очень похоже на ваш пример. Таким образом, если у вас есть действительный объект, у вас есть ресурс.

Если объект построен, значит, вы успешно приобрели ресурс. Это означает, что для жизни объекта вы владеете ресурсом. Когда объект удаляется, ресурс освобождается. Если объект никогда не создается, то вы никогда не приобретали ресурс. Смотрите википедию:

http://en.wikipedia.org/wiki/Resource_Acquisition_Is_Initialization

1 голос
/ 04 ноября 2009

Из стандарта C ++ 2003 5.3.4 / 17 - Новое:

Если какая-либо часть инициализации объекта, описанная выше, завершается выдачей исключения и может быть найдена подходящая функция освобождения, функция освобождения вызывается для освобождения памяти, в которой создавался объект, после чего исключение продолжает распространяться в контексте нового выражения. Если не может быть найдена однозначная совпадающая функция освобождения, распространение исключения не приводит к освобождению памяти объекта. [Примечание: это уместно, когда вызываемая функция выделения не выделяет память; в противном случае это может привести к утечке памяти. ]

Таким образом, возможна или не может быть утечка - это зависит от того, может ли быть найден соответствующий деаллокатор (что обычно происходит, если оператор new / delete не был переопределен). В случае, когда есть подходящий деаллокатор, Компилятор отвечает за подключение в вызове, если конструктор выдает.

Обратите внимание, что это более или менее не связано с тем, что происходит с ресурсами, полученными в конструкторе, и именно это обсуждалась моя первая попытка ответа - и это вопрос, который обсуждается во многих часто задаваемых вопросах, статьях и публикациях.

...