Бросок bad_alloc
имеет два эффекта:
- Это позволяет поймать и обработать ошибку где-то в иерархии вызывающего объекта.
- Он производит четко определенное поведение, независимо от того, происходит ли такая обработка.
По умолчанию для этого четко определенного поведения процесс завершается ускоренным, но упорядоченным образом путем вызова std::terminate()
. Обратите внимание, что это определяется реализацией (но, тем не менее, для данной реализации четко определено), будет ли раскручен стек перед вызовом terminate()
.
Это сильно отличается от необработанного сбоя malloc()
, например, который (а) приводит к неопределенному поведению, когда возвращенный нулевой указатель разыменован, и (б) позволяет беспечно продолжать выполнение до (и после) этого момента, обычно накапливая дополнительное выделение неудачи на пути.
Следующий вопрос заключается в том, где и как вызывающий код должен перехватывать и обрабатывать исключение.
В большинстве случаев ответ заключается в том, что не должно.
Что будет делать обработчик? На самом деле есть два варианта:
- Завершите приложение более упорядоченным образом, чем обработка необработанных исключений по умолчанию.
- Освободите часть памяти в другом месте и повторите попытку выделения.
Оба подхода усложняют систему (особенно последний), что необходимо обосновывать в конкретных обстоятельствах и, что важно, в контексте других возможных режимов отказа и способов его устранения. (Например, критическая система, которая уже содержит непрограммные отказоустойчивые устройства, может быть лучше быстро завершить работу, чтобы эти механизмы сработали, а не возиться с программным обеспечением.)
В обоих случаях, вероятно, имеет больше смысла, чтобы любая фактическая обработка выполнялась выше в иерархии вызывающих объектов, чем в точке, в которой произошло неудачное выделение.
И если ни один из этих подходов не дает никаких преимуществ, то лучший подход — просто включить обработку по умолчанию std::terminate()
.
person
Jeremy
schedule
23.07.2019