New (std::nothrow) против New в блоке try / catch
Я провел некоторое исследование после того, как узнал new
, в отличие от malloc()
, к которому я привык, не возвращает NULL для неудачных распределений, и обнаружил, что есть два различных способа проверить, удалось ли new или нет. Эти два способа таковы:
try
{
ptr = new int[1024];
}
catch(std::bad_alloc& exc)
{
assert();
};
И
ptr = new (std::nothrow) int[1024];
if(ptr == NULL)
assert();
Я верю, что эти два способа достигают одной и той же цели (поправьте меня, если я ошибаюсь, конечно!), поэтому мой вопрос таков:
Который является лучшим вариантом для проверки, если new
удалось, полностью основанный на читабельности, ремонтопригодность и производительность, игнорируя при этом фактическое соглашение о программировании на языке c++.
4 ответа:
Считаю, что вы делаете. Вы распределяете память. И если по какой-то причине выделение памяти не может работать, вы
Поэтому задайте себе вопрос: действительно ли вамнужно заботиться о том, что произойдет, если вы кончилась память? Если все, что вы делаете, это утверждение, то метод исключения лучше, потому что он не загромождает ваш код случайнымиassert
. Что более или менее точно произойдет, если вы просто позволитеstd::bad_alloc
распространиться обратно вmain
. В сборке выпуска, гдеassert
является no-op, ваша программа аварийно завершит работу, когда она попытается получить доступ к памяти. Таким образом, это то же самое, что позволить исключению всплывать: остановка приложения.assert
s. вы просто позволяете исключению вернуться кmain
.Если у вас действительно есть специальный кодовый путь в случае, если вы не можете выделить память (то есть вы можете фактически продолжать функционировать), исключения могут быть или не быть способом пойти, в зависимости от того, что такое кодовый путь. Если кодовый путь - это просто переключатель, установленный с помощью указателя null, то
nothrow
версия будет проще. Если вместо этого вам нужно сделать что-то совсем другое (вытащить из статического буфера, или удалить какой-то материал, или что-то еще), то ловитьstd::bad_alloc
довольно хорошо.
Это зависит от контекста, в котором происходит выделение. Если ваша программа может продолжить работу даже в случае сбоя выделения (возможно, вернуть код ошибки вызывающему объекту), то используйте метод
std::nothrow
и проверьте значение NULL. В противном случае вы бы использовали исключения для потока управления, что не является хорошей практикой.С другой стороны, если ваша программа абсолютно нуждается в том, чтобы эта память была успешно выделена для того, чтобы иметь возможность функционировать, используйте
try-catch
, чтобы перехватить (не обязательно в непосредственной близости окрестностиnew
) исключения и изящно выйти из программы.
С точки зрения чистой производительности это имеет мало значения. Существует неотъемлемая накладная стоимость обработки исключений, хотя эта накладная стоимость обычно стоит компромисса в удобочитаемости приложения и обслуживании. Сбои выделения памяти такого рода не должны быть в 99% случаев вашего приложения, поэтому это должно происходить нечасто.
С точки зрения производительности вы обычно хотите избежать стандартного распределителя из-за его относительно низкой производительности в любом случае.
Все тем не менее, я обычно принимаю версию исключения, потому что обычно наши приложения находятся в состоянии, когда в случае сбоя выделения памяти мы мало что можем сделать, кроме как изящно выйти с соответствующим сообщением об ошибке, и мы экономим производительность, не требуя
NULL
проверки наших новых выделенных ресурсов, потому что по определению сбой выделения переместит область, где это имеет значение.
new
используется для создания объектов, а не выделения памяти, поэтому ваш пример несколько искусственен.Конструкторы объектов обычно бросают, если они терпят неудачу. Пройдя через реализацию
new
в Visual Studio более нескольких раз, я не верю, что код улавливает какие-либо исключения. Поэтому обычно имеет смысл искать исключения при создании объектов.Я думаю
Разница в производительности между двумя подходами, вероятно, не имеет значения, так как большая часть процессорного времени может быть легко потрачена в конструкторе объекта или поиске кучи. Эмпирическое правило не всегда уместно. Например, системы реального времени обычно ограничивают динамическое выделение памяти, поэтомуstd::bad_alloc
выбрасывается только в случае сбоя части выделения памяти. Я не знаю, что именно. бывает, если вы передаетеstd::nothrow
вnew
, но конструктор объекта бросает-в документах, которые я читал, есть неоднозначность.new
, если он присутствует, вероятно, будет перегружен. В в этом случае он может использовать возвращенный нулевой указатель и обрабатывать сбой локально.