Динамическая память и исключения конструктора - PullRequest
5 голосов
/ 05 июля 2010

Рано сегодня я обнаружил функциональные блоки try-catch (от здесь * на самом деле ), а затем немного углубился в исследование - очевидно, они используются главным образом для того, чтобы ловить исключения, создаваемые конструктором список инициализаторов.

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


Данный пример кода:

class A
{
private:
    B b
    C *c;    //classes B, C & D omitted for brevity as not really relevant
    D d;
public
    A(int x, int y, int z)
};

A::A(int x, int y, int z)
try
    : b( x )
    , c( new C(y) )
    , d( z )
{
    //omitted
}
catch(...)
{
    //omitted
}

Что происходит в этих случаях:

  1. Инициализация b вызывает исключение.
  2. Инициализация c вызывает исключение.
  3. Инициализация d вызывает исключение.

В частности, я хочу знать, по крайней мере:

  • что может / может вызвать утечку памяти у new C(y). Я думаю только 3? (см. здесь )
  • не могли бы вы просто delete b в улове? Опасно в случаях 1 и 2?

Очевидно, я думаю, что безопаснее всего сделать c умным указателем. Но не обращая внимания на этот вариант на данный момент, каков наилучший курс действий?

Безопасно ли установить c на NULL в инициализаторе, а затем поместить вызов на new в теле конструктора?

Это тогда означало бы, что delete c должно быть помещено в зацепку на случай, если что-то еще добавится в тело конструктора? Есть ли проблемы с безопасностью при этом (т. Е. Если выбрасывает сам c = new C(y);)?

Ответы [ 3 ]

8 голосов
/ 05 июля 2010

Функциональные блоки try / catch не одобряются так же, как и goto - может быть какой-то угловой случай, когда они имеют смысл, но их лучше избегать: когда не удается построить объект, лучше всего сделать терпит неудачу и терпит неудачу быстро.

По конкретным вопросам, когда в конструкторе выдается исключение, все полностью построенные подобъекты будут уничтожены. Это означает, что в случае b он будет уничтожен, в случае c, который является необработанным указателем, ничего не делается. Самое простое решение - изменить c на интеллектуальный указатель, который обрабатывает выделенную память. Таким образом, если d сгенерирует, умный указатель будет уничтожен, а объект освобожден. Это не связано с блоком try / catch, а скорее с тем, как работают конструкторы.

Также, как правило, небезопасно удалять указатель из блока catch, так как нет гарантии действительного значения указателя до инициализации. То есть, если b или c throw, это может быть случай, когда c не равен 0, но также не является допустимым указателем, и удаление его будет неопределенным поведением. Как всегда, бывают случаи, как если бы у вас была гарантия того, что ни b, ни c не будет выброшено, и если предположить, что bad_alloc не является чем-то, от чего вы обычно восстанавливаетесь, тогда это может быть безопасно.

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

2 голосов
/ 05 июля 2010

Вы не можете ничего сделать в обработчике функции try-block, кроме как перевести одно исключение в другое.Вы не можете предотвратить исключение.Вы ничего не можете сделать с учениками.Поэтому нет, вы не можете сделать delete c в функции-блоке-конструкторе.

Полностью построенные базовые классы и члены объекта должны быть уничтожены перед входом в обработчик функции-try-блок конструктора или деструктора для этого объекта.

и

Текущее обработанное исключение перебрасывается, если управление достигает конца обработчика функции-try-blockконструктора или деструктора.В противном случае функция возвращается, когда управление достигает конца обработчика для функции-try-block (6.6.3).Вытекающий из конца функционального try-block эквивалентен возврату без значения;это приводит к неопределенному поведению в функции, возвращающей значение.

(15.3 [исключением. дескриптор] из стандарта C ++)

0 голосов
/ 05 июля 2010

Чтобы ответить на ваш фактический вопрос, всякий раз, когда возникает исключение во время построения, выполнение останавливается тут же (конструируемый объект никогда не создается) и управление переходит к ближайшему обработчику исключений (catch).

Вы правы, что утечка памяти произойдет только тогда, когда d вызовет исключение, и в этом случае внешний catch необходимо очистить.

Если сам new C выдает, объектникогда не создавался, поэтому вам не нужно беспокоиться об его удалении.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...