Рано сегодня я обнаружил функциональные блоки 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
}
Что происходит в этих случаях:
b
выдает исключение.c
выдает исключение.d
выдает исключение.А именно, я хочу знать, по крайней мере:
new C(y)
. Я думаю только 3? (см. здесь),delete b
в выгоде? Опасно в случаях 1 и 2?Очевидно, я предполагаю, что самая безопасная вещь сделать состоит в том, чтобы сделать c
интеллектуальный указатель. Но игнорируя ту опцию в настоящий момент, каков лучший план действий?
Действительно ли безопасно установить c
кому: NULL
в инициализаторе, и затем помещают вызов в new
в теле конструктора?
Это затем означало бы a delete c
должен быть помещен в выгоду в случае, если что-то еще добавляет тело конструктора? Есть ли проблемы безопасности, делающие это (т.е., если это c = new C(y);
самостоятельно это бросает)?
Функциональные блоки try / catch не одобряются, так же как и goto - могут быть некоторые угловые случаи, когда они имеют смысл, но их лучше избегать: когда объект не может быть сконструирован, лучшее, что вы можете сделать, это терпеть неудачу и терпеть неудачу быстро.
По вашим конкретным вопросам, когда в конструкторе создается исключение, все полностью построенные подобъекты будут уничтожены. Это означает, что в случае b
он будет уничтожен, в случае c
, поскольку это необработанный указатель, ничего не будет сделано. Самое простое решение - заменить c
интеллектуальным указателем, который обрабатывает выделенную память. Таким образом, если d
выбрасывается, интеллектуальный указатель будет уничтожен, а объект освобожден. Это не связано с блоком try / catch, а скорее с тем, как работают конструкторы.
Также, как правило, небезопасно удалять указатель из блока перехвата, поскольку нет гарантии фактического значения указателя до того, как будет выполнена инициализация.То есть, если b
или c
throw, может случиться так, что c
не равно 0, но также не является допустимым указателем, и его удаление будет быть неопределенным поведением. Как всегда, бывают случаи, как будто у вас есть гарантия, что ни b
, ни c
не сработают, и если предположить, что bad_alloc
- это не то, из чего вы обычно восстанавливаетесь , тогда это может быть безопасно.
Обратите внимание, что если вам нужно сохранить указатель с необработанным указателем по какой-либо конкретной причине, лучше инициализировать его значением 0 в списке инициализации, а затем создать объект внутри строительного блока, чтобы избежать этой проблемы. Помните также, что при хранении более одного (необработанного) ресурса непосредственно в классе очень сложно гарантировать отсутствие утечки ресурсов - если первый ресурс создается и назначается, а второй выходит из строя, деструктор не будет вызываться, и ресурс будет протекать. Опять же, если вы можете использовать умный указатель, у вас будет на одну проблему меньше.
Вы не можете ничего делать в обработчике функционального блока попытки, кроме как транслировать одно исключение в другое. Вы не можете предотвратить создание исключения. Вы ничего не можете сделать с учениками. Так что нет, вы не можете выполнить delete c
в блоке-try-конструкторе конструктора.
Полностью построенные базовые классы и члены объекта должны быть уничтожается до попадания в обработчик функционально-пробного блока конструктор или деструктор для этого объект.
и
Текущее обрабатываемое исключение: переброшен, если контроль достигает конца обработчик функционально-пробного блока конструктор или деструктор. В противном случае функция вернется, когда элемент управления достигает конца обработчика для функционального-пробного-блока (6.6.3). Сливаясь с конца function-try-block эквивалентен возврат без значения; это приводит к неопределенное поведение при возврате значения функция.
(15,3 [except.handle] из стандарта C ++)
Чтобы ответить на ваш вопрос, всякий раз, когда во время конструирования возникает исключение, выполнение останавливается прямо на этом месте (конструируемый объект никогда не конструируется), а управление передается ближайшему обработчику исключений (catch
).
Вы правы, что утечка памяти произойдет только тогда, когда d
вызовет исключение, и в этом случае внешний catch
должен очиститься.
Если new C
сам бросает, объект никогда не будет создан, поэтому вам не нужно беспокоиться о его удалении.