Новый (std :: nothrow) против нового в блоке try / catch - PullRequest
39 голосов
/ 02 сентября 2011

Я провел некоторое исследование после изучения 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 ]

49 голосов
/ 02 сентября 2011

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

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

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

11 голосов
/ 02 сентября 2011

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

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

7 голосов
/ 02 сентября 2011

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

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

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

0 голосов
/ 03 сентября 2014

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

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

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

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

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

...