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, ваша программа аварийно завершит работу, когда она попытается получить доступ к памяти. Таким образом, это то же самое, что позволить исключению всплывать: остановка приложения.asserts. вы просто позволяете исключению вернуться к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, если он присутствует, вероятно, будет перегружен. В в этом случае он может использовать возвращенный нулевой указатель и обрабатывать сбой локально.