Динамическая память и исключения конструктора

Рано сегодня я обнаружил функциональные блоки 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 в теле конструктора?

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

5
задан Community 23 May 2017 в 11:47
поделиться

3 ответа

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

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

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

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

8
ответ дан 13 December 2019 в 22:01
поделиться

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

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

и

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

(15,3 [except.handle] из стандарта C ++)

2
ответ дан 13 December 2019 в 22:01
поделиться

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

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

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

0
ответ дан 13 December 2019 в 22:01
поделиться
Другие вопросы по тегам:

Похожие вопросы: