Nothrow или исключение? - PullRequest
       32

Nothrow или исключение?

9 голосов
/ 31 декабря 2010

Я студент, и у меня есть небольшие знания по C ++, которые я пытаюсь расширить.Это скорее философский вопрос. Я не пытаюсь что-то реализовать.

Поскольку

#include <new> 
//...
T * t = new (std::nothrow) T();
if(t)
{
    //...
}
//...

Будет скрывать Исключение, и поскольку работа с Исключениями более тяжелая по сравнению с простым if(t), почему обычная new T() не считается менее хорошей практикой, учитывая, что нам придется использовать try-catch(), чтобы проверить, удалось ли простое распределение (а если нет, просто посмотреть, как программа умирает) ??

Каковы преимущества (если есть) нормального распределения new по сравнению с использованием nothrow new?Затраты на исключение в этом случае незначительны?

Кроме того, предположим, что распределение не выполнено (например, в системе нет памяти).Есть ли что-нибудь, что может сделать программа в этой ситуации, или просто изящно провалиться.Там нет никакого способа найти свободную память в куче, когда все зарезервировано, есть ли?

В случае, если выделение не удается, и std::bad_alloc равно throw n, как мы можем предположить, что, посколькунедостаточно памяти для выделения объекта (например, new int), будет достаточно памяти для хранения исключения ??

Спасибо за ваше время.Я надеюсь, что вопрос соответствует правилам.

Ответы [ 6 ]

8 голосов
/ 31 декабря 2010

С иметь дело с исключениями тяжелее по сравнению с простым if (t), почему не нормальный новый T () не считается меньше хорошая практика, учитывая, что мы будем должен использовать try-catch (), чтобы проверить, если простое распределение прошло успешно (и если мы не, просто смотри как программа умирает) Каковы преимущества (если таковые имеются) нормальное новое распределение по сравнению с используя новую? Исключение-х накладные расходы в этом случае незначительны

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

Преимущество исключений состоит в том, что ваш код проще: если вы выделяете несколько объектов, вам не нужно делать «распределить A; if (A) {выделить B; if (B) и т. Д.»). Очистка и завершение - как в случае исключения, так и в случае основной линии - лучше всего обрабатывать автоматически с помощью RAII (в то время как при проверке вручную вам также придется освободить вручную, что слишком упрощает утечку памяти).

Кроме того, предположим, что распределение не выполнено (например, в системе нет памяти). Есть ли что-нибудь, что может сделать программа в такой ситуации или просто не получится изящно. Там нет способа найти свободной памяти в куче, когда все будет зарезервировано, есть?

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

, если выделение не удастся, и std :: bad_alloc брошен, как мы можем Предположим, что так как недостаточно память для выделения объекта (например, новый int), памяти будет достаточно хранить исключение ??

Нет, обычно стандартные библиотеки гарантируют, обычно путем предварительного выделения небольшого объема памяти, что будет достаточно памяти для исключения в случае ее исчерпания.

6 голосов
/ 01 января 2011

Nothrow был добавлен в C ++ главным образом для поддержки разработчиков встроенных систем, которые хотят писать код без исключений. Это также полезно, если вы действительно хотите обрабатывать ошибки памяти локально, как лучшее решение, чем malloc (), за которым следует размещение new. И, наконец, это важно для тех, кто хотел продолжать использовать (то, что было тогда актуально) стили программирования C ++, основанные на проверке на NULL. [Я сам предложил это решение, одну из немногих предложенных мной вещей, которые не получили отрицательного ответа:]

К вашему сведению: выбрасывание исключения из памяти очень чувствительно к дизайну и его трудно реализовать, потому что, например, если вы, например, выбросите строку, вы можете удвоить ошибку, поскольку строка выполняет выделение кучи. Действительно, если у вас недостаточно памяти из-за того, что ваша куча врезалась в стек, вы даже не сможете создать временный! Этот конкретный случай объясняет, почему стандартные исключения довольно ограничены. Кроме того, если вы ловите такое исключение довольно локально, почему вы должны ловить по ссылке, а не по значению (чтобы избежать возможного копирования, вызывающего двойную ошибку).

Из-за всего этого nothrow обеспечивает более безопасное решение для критически важных приложений.

4 голосов
/ 31 декабря 2010

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

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

Наконец, обычно можно выдать исключение, даже если памяти недостаточно. Многие реализации C ++ резервируют некоторое пространство в стеке (или некотором другом сегменте памяти без кучи) для исключений, поэтому даже если куче не хватает места, можно найти память для исключений.

Надеюсь, это поможет!

3 голосов
/ 31 декабря 2010

Обход исключений, потому что они «слишком дороги», является преждевременной оптимизацией. Затраты на попытку / отлов практически отсутствуют, если исключение не выдается.

Есть ли что-нибудь, что может сделать программа в этой ситуации

Не обычно. Если в системе нет памяти, вы, вероятно, даже не можете записать что-либо в журнал, распечатать на стандартный вывод или что-то еще. Если у вас не хватает памяти, вы в значительной степени облажались.

2 голосов
/ 31 декабря 2010

Предполагается, что нехватка памяти будет редким событием, поэтому дополнительные затраты на создание исключения, когда это происходит, не являются проблемой.Реализации могут «предварительно выделить» любую память, необходимую для выброса std::bad_alloc, чтобы обеспечить ее доступность даже в том случае, если программе не хватило памяти.

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

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

0 голосов
/ 31 декабря 2010

В Symbian C ++ все работает наоборот. Если вы хотите, чтобы при OOM было сгенерировано исключение, вы должны сделать

T* t = new(ELeave) T();

И вы правы относительно логики создания нового исключения, когда OOM странный. Управляемый сценарий внезапно становится завершением программы.

...