Подтвердить что ты не робот

Новый (std:: nothrow) против New в блоке try/catch

Я провел некоторое исследование после изучения new, в отличие от malloc(), к которому я привык, не возвращает NULL для неудачных распределений, и обнаружил, что существуют два разных способа проверить, выполнено ли новое или нет. Этими двумя способами являются:

try
{
    ptr = new int[1024];
}
catch(std::bad_alloc& exc)
{
    assert();
};

и

ptr = new (std::nothrow) int[1024];
if(ptr == NULL) 
    assert();

Я считаю, что два пути достигают той же цели (исправьте меня, если я ошибаюсь, конечно!), поэтому мой вопрос таков:

что является лучшим вариантом для проверки успеха new, основанного исключительно на читабельности, ремонтопригодности и производительности, при этом игнорируя условное соглашение о программировании на языке С++.

4b9b3361

Ответ 1

Подумайте, что вы делаете. Вы выделяете память. И если по какой-то причине распределение памяти не может работать, вы assert. Что более или менее точно, что произойдет, если вы просто передадите std::bad_alloc обратно на main. В сборке релизов, где assert - нет-op, ваша программа выйдет из строя, когда попытается получить доступ к памяти. Так что это то же самое, что позволить исключению пузыря: остановка приложения.

Поэтому задайте себе вопрос: вам действительно нужно заботиться о том, что произойдет, если у вас закончится память? Если все, что вы делаете, это утверждение, то метод исключения лучше, потому что он не загромождает ваш код случайными assert s. Вы просто разрешите исключение вернуться к main.

Если у вас действительно есть специальная кодировка в случае, если вы не можете выделить память (то есть, вы действительно можете продолжать функционировать), исключения могут быть или не быть способом, в зависимости от того, что такое кодировка. Если codepath - это просто переключатель, установленный с указателем, равным нулю, то версия nothrow будет проще. Если вместо этого вам нужно сделать что-то совсем другое (вытащите из статического буфера или удалите некоторые вещи или что-то еще), тогда ловить std::bad_alloc неплохо.

Ответ 2

Это зависит от контекста того, где происходит распределение. Если ваша программа может продолжаться, даже если распределение не удается (возможно, вернуть код ошибки вызывающему абоненту), используйте метод std::nothrow и проверьте значение NULL. В противном случае вы будете использовать исключения для потока управления, что не является хорошей практикой.

С другой стороны, если ваша программа абсолютно нуждается в том, чтобы эта память была успешно распределена, чтобы иметь возможность функционировать, используйте try-catch для улова (не обязательно в непосредственной близости от new) исключения и выхода изящно из программы.

Ответ 3

С точки зрения чистой производительности это имеет мало значения. При обработке исключений есть накладные накладные расходы, хотя эти накладные расходы в целом заслуживают компромиссов в отношении удобочитаемости и обслуживания приложений. Ошибки выделения памяти такого рода не должны быть в 99% случаев вашего приложения, поэтому это должно происходить нечасто.

С точки зрения производительности вы обычно хотите избежать стандартного распределителя из-за его относительно низкой производительности в любом случае.

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

Ответ 4

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

Конструкторы объектов обычно бросают, если они терпят неудачу. Пройдя через реализацию new в Visual Studio более чем несколько раз, я не считаю, что код ловит какие-либо исключения. Поэтому, как правило, имеет смысл искать исключения при создании объектов.

Я думаю, что std::bad_alloc выбрасывается только в случае сбоя части памяти. Я не уверен, что произойдет, если вы пройдете std::nothrow до new, но конструктор объектов бросает - в документах, которые я прочитал, есть двусмысленность.

Разница в производительности между двумя подходами, вероятно, не имеет значения, так как большая часть процессорного времени может быть легко потрачена на конструктор объекта или на поиск кучи.

Правило большого пальца не всегда подходит. Например, системы реального времени обычно ограничивают распределение динамической памяти, поэтому new, если она присутствует, вероятно, будет перегружена. В этом случае он может использовать возвращенный нулевой указатель и локальный сбой.