5

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

С

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

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

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

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

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

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

+0

Если новый код не работает в вашем коде выше. Что вы планируете делать в заявлении if? На этом этапе невозможно исправить ошибку. –

+0

@ Мартин, ничего действительно. Мне было просто интересно узнать об этом случае, и если есть преимущество в использовании 'nothrow'. На самом деле ответы сделали многие вещи ясными. – Muggen

+1

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

ответ

5

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

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

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

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

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

Incase выделения терпит неудачу, и станда :: bad_alloc брошен, как мы можем предположить, что, поскольку не хватает памяти для размещения объекта (например, в нового Int), будет достаточно памяти для хранения исключения ??

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

+2

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

+0

@ Мартин Йорк: Вы правы, «тяжелый» - действительно загруженное слово :) Но трудно быть более конкретным при решении таких общих вопросов, как это - точный штраф будет зависеть от реализации, платформы и от числа тех десяти вызовов функций, которые улавливают и перебрасывают исключение. Вы могли бы поспорить, что стоимость будет одинаковой, и вы можете быть правы; если бы я был в ситуации, когда я достаточно заботился об этой разнице, я бы ее измерил :-) – psmears

+0

@Martin: Исключения ужасно дороже. Я был бы удивлен, если бы проверять десять возвращаемых значений было даже заметно по сравнению с исключением. Он проверяет эти десять возвращаемых значений в течение 100 000 успешных операций, которые хуже, чем исключение. Поэтому для проверки данных, предоставленных пользователем, предпочтительными являются возвращаемые значения, поскольку отказ является относительно частым. Сетевые операции снова выходят довольно часто, поэтому возвращайте значения. Выделение, никогда не терпит неудачу ***, поэтому идите с исключением. [*** Сноска: большинство систем будут до смерти до исчерпания адресного пространства] –

2

Исключение из-за того, что они «слишком дороги» - это преждевременная оптимизация. Практически нет накладных расходов на try/catch, если исключение не выбрасывается.

Что программа может сделать в этой ситуации

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

+0

Аргумент «преждевременной оптимизации» - это лозунг предыдущего века, который убивает любую разумную дискуссию до того, как у нее даже был шанс. Например, в критически важных средах, где стабильность является ключевой, вы действительно не хотите, чтобы куча неизвестной обработки исключений уничтожала поток вашего программного обеспечения. – StarShine

+0

@StarShine: Это достойный аргумент. Но исключения, «слишком дорогие» в общем случае, не то, о чем вам следует беспокоиться. – Falmarri

+0

Мне когда-то учили соглашаться с вашим заявлением, но что думать о 1) «общем случае» все чаще не гарантирует использование C++ и 2) семантического значения того, что «исключение» имеет тенденцию варьироваться в зависимости от ваш пробег/язык программирования. Я имею в виду, что принцип хорош, и он может сэкономить время разработки, если все поймут то же самое. На практике .. – StarShine

2

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

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

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

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

-2

В Symbian C++ это работает наоборот. Если вы хотите исключение возникающего при ООМ вы должны сделать

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

И вы правы насчет логики метания нового исключения, когда ОЫЙ быть странным. Сценарий, который управляется, внезапно становится завершением программы.

+2

Это говорит только о том, что Symbian C++ на самом деле не является стандартным C++. Теперь аргументы за коды ошибок вместо исключений очень старые и неоднократно показывались как ошибочные. Краткий обзор можно найти здесь: http://www.boost.org/community/exception_safety.html –

+0

Неверно? Lol, это похоже на то, что автомобили с коробкой передач с коробкой передач ошибочны. – James

1

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

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

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

5

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

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

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

Смежные вопросы