Std bad alloc что это
Перейти к содержимому

Std bad alloc что это

Исключения / C++ для начинающих / C++

Исключения традиционно относятся к сложной для понимания части C++. В форумах часто возникают вопросы на эту тему.. в общем – статья назрела.
Я не претендую на авторство высказанных здесь мыслей, я просто собрал всё вместе.

Исключение – это явление, которое происходит при ненормальном развитие событий в программе и требует особой логики обработки. В идеале правильно спроектированная программа не нуждается в обработке исключений. Дело в том, что ситуации, ведущие к исключениям, можно отлавливать на ранних стадиях, анализируя все возвращаемые функциями коды ошибок. Если в вашей программе вы хотите отгородится исключением от попыток деления на 0 или выделения –200 байт (читается «минус двухсот байт»), то вы находитесь далеко от правильного пути. И в этом случае лучше доработать алгоритм работы. Заранее проверить «Сколько байт я хочу выделить?». Заранее проверить, «а на что же я собираюсь поделить?». Стандарт С (ISO/IEC 9899) не содержит обработку исключений. Это ещё один довод в пользу того, что правильно спроектированная программа должна правильно работать и без них.

Живём мы в реальном мире, поэтому обработка исключений нам нужна. Операционная система не смогла выделить те 500 Мб, которые вы попросили. Драйвер не оказался загруженным. Файла не оказалось в нужное время в нужном месте. Да мало ли ещё что произошло! И вот тут нас спасёт обработка исключений. Кроме того, иногда исключения упрощают вывод сообщений об ошибках в программе, например, при использовании MFC исключений. Об этом — впереди. В общем, исключения – это дешёвый способ существенно повысить устойчивость вашего (особенно системного или серверного) программного обеспечения.

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

Вопрос. Есть ли в С обработка исключений?
Обработка исключений есть только в C++. Стандарт С (ISO/IEC 9899) её не содержит.

Вопрос. Кто берёт на себя ответственность за обработку исключений?
CRTL – C-Run-Time-Library.

Вопрос. Что такое SEH?
SEH – Structured Exception Handling – в операционной системе Windows включена обработка исключений на уровне операционной системы. Блоки SEH оформляются с помощью операторов __try, __finally, __except. Если SEH-исключение не перехвачено, то произойдет появление хорошо известного окна с предложением впаять разработчику и остановка процесса.

Вопрос. Чем плохо использовать операторы __try, __except и т.д.
Плохая совместимость с программами, написанными на «чистом» С++. В пределах одной функции невозможно пользоваться CRTL и SEH исключениями.
Если вы работаете с Visual Studio 6, CRTL преобразует стандартное исключение в SEH исключения в случае, если при сборке проекта указан ключ /EHa или (эквивалентный) /GX, и установки галочки Enable Exception Handling в состояние No. В Visual Studio 7: заходим на вкладку свойств проекта C/C++ Code Generation, находим строчку Enable C++ exception, ставим в этой строчке No. Дальше двигаемся в конец к секции Command Line. В ней есть окошко Additional Options. Надо прописать /EHac.
Недостаток этого подхода — невозможно определить тип исключения, и ,что более серьёзно, возникают проблемы с плавающей арифметикой (последнее утверждение не проверял, но поверил одному товарищу). Имеется более усовершенствованный метод. Заключается он в использовании так называемого se транслятора. Вот примерный код (Visual Studio).

static void se_translator(unsigned int code,_EXCEPTION_POINTERS *)
<
if( code == EXCEPTION_FLT_DENORMAL_OPERAND ||
code == EXCEPTION_FLT_DIVIDE_BY_ZERO ||
code == EXCEPTION_FLT_INEXACT_RESULT ||
code == EXCEPTION_FLT_INVALID_OPERATION ||
code == EXCEPTION_FLT_OVERFLOW ||
code == EXCEPTION_FLT_STACK_CHECK ||
code == EXCEPTION_FLT_UNDERFLOW)
<
short cw=. ;

void Setup_SE_translator()
<
_set_se_translator(se_translator);
>

Смысл кода приблизительно в следующем. Вызовом функции _set_se_translator можно установить функцию, которая будет получать управление в случае возникновения в текущем потоке SEH-исключения. Главное назначение этой функции — получить код SEH-исключения, завернуть в подходящую обёртку и выбросить нормальное C++ исключение, которое в дальше можно поймать обычным catch(). Коды этих исключений можно получить из windows.h, а описание — в MSDN в статье про EXCEPTION_RECORD, либо в прикрепленном файле. Среди этих кодов есть семейство особо важных, связанных с плавающей точкой. При получении одного из этих кодов, надо делать маленькую дополнительную обработку. А именно, нужно командой fninit сбросить сопроцессор в нормальное состояние и загрузить подходящее слово управление. Иначе флаги исключений по-прежнему будут висеть в сопроцессоре, что вызовет возбуждение нового исключения при попытке его снова использовать – Вам оно нужно? Вообще, использование исключений сопроцессора — это отдельный нетривиальный вопрос.

Вопрос. Я работаю с STL, очень часто использую операцию push_back(), при этом не знаю, как контролировать ситуацию, когда память push_back – ом не выделена, потому что push_back не возвращает код ошибки. Как мне быть?

Всё нормально – вам необходимо ловить исключение std::bad_alloc – именно оно генерируется в случае неудачного проведения операции push_back. И не только push_back() – но и вообще везде, где STL перераспределяет память – например, resize().

//код приведён для Visual C++, в иных компиляторах возможны изменения//
#include <new>
#include <new.h>

//функция установки new handler-a.
int _cdecl my_new_handler(size_t)
<
throw std::bad_alloc();
return 0;
>

//это в какой нибудь функции:

_PNH _old_new_handler;
_old_new_handler = _set_new_handler(my_new_handler);

//тут new будет кидать исключения std::bad_alloc

Обязательно ли возвращать old_new_handler на место – не знаю, скорее всего необязательно. По моему — лучше всего это сделать один раз в самом начале программы, а по завершении – вернуть old_new_handler. С другой стороны производительность стандартного оператора new (как и всего остального стандартного) немного хромает, если вы желаете добиться экстра производительности – то old_new_handler лучше вернуть на место. В общем – я предупредил – остальное на вашей совести.

4) Вы работаете с MFC. В этом случае вы можете поймать только указатель на исключение CException либо производный от него. В этом случае, если вы будете, например, пытаться выделить большое количество памяти, то MFC будет упорно кидать сообщение «Out of memory». И с этим ничего поделать нельзя – придётся ловить MFC исключения (не помогает даже ручная установка new handler-а), это видимо сделано под девизом «Мы в Майкрософт, всегда считаем, что стандарт можно улучшить» (Copyright кто то из MS, но не Билл Гейтс);

Вопрос. У меня что то случилось с размером контейнера при вызове исключения std::bad_alloc – size() вернул одно, а перечисление с помощью итератора – на один элемент больше.
Такое бывает если исключение кидает конструктор копии – size() не учитывает недоконструированный элемент, а при перечислении он может и остаться, это касается контейнеров std::list, std::dequе и других. Это – «особенность дизайна» некотрых реализаций STL, например, той, что поставляется с Visual C++. Exception safety контейнеров стандартной библиотеки была добавлена в последний момент процесса стандартизации, поэтому далеко не все реализации контейнеров правильно ведут себя в присутствии исключений. Так версия STL от Dinkumware, что поставляется с VC 6 тянется ещё со времен VC 4.2, т.е. года 1994 — последняя версия стандарта C++ вышла в 1998 году (комментировать нужно?). Бороться с этим можно путём обновления STL на более свежую реализацию (например, от STLPort – www.stlport.com). Либо не бросать исключения в конструкторах.

Вопрос. Перечислите плюсы и минусы использования SEH по сравнению с обычными CRTL исключениями.
Плюсы:
1) позволяет ловить больший спектр исключений, к которым относится деление на 0, переполнение стека, и т.д.
2) обработка исключений ведётся на уровне ядра операционной системы (в WinNT образных ОС);
3) возможность использовать исключения без CRTL. Часто для уменьшения размера программы её собирают без CTRL. В этом случае использовать «стандартные» C++ исключения невозможно. SEH можно будет воспользоваться, если загрузить kernel32.dll.

Минусы:
1) плохая совместимость с С++. SEH исключения реализованы на уровне ядра ОС, которое ничего не знает про С++, например про классы. Если произошла исключительная ситуация, то SEH не гарантирует, что уберёт за собой весь мусор, потому что не будут вызваны деструкторы пользовательских классов. Это связано с тем, что если компилятор не видит генерации C++-исключений, то он и не создает код, который отвечает за размотку стека при исключениях (только при использовании слов __try, __except и т.д.).
2) невозможность (в пределах одной функции) пользоваться SEH и стандартными исключениями одновременно.

Вопрос. Как использовать SEH исключения?

__try
<
//.
>
__except(GetExceptionCode() == ….) //подставить нужное слово
<
//.
>

__except(EXCEPTION_EXECUTE_HANDLER) //подставить нужное слово
<
//.
>

GetExceptionCode() – возвращает код возникшего исключения – можно использовать для вывода диагностического сообщения. Если Вы используете слово __finaly, то этот блок будет выполнен в любом случае, даже если попытаться выйти из блока __try с помощью return;
В одном блоке __except и __finaly одновременно быть не могут.

Кроме того, можно получить машинно-независимую информацию об исключении, при помощи функции GetExceptionInformation().

Структурная обработка особых ситуаций средствами Win32 API

Вопрос. Как ловить MFC исключения?

CFile f;
CFileException *pE = new CFileException;
TCHAR szErrorString[255];

if (f.Open(m_sDraftName, CFile::modeRead | CFile::shareDenyWrite, pE) == FALSE)
<
pE->ReportError(MB_OK | MB_ICONSTOP);
pE->GetErrorMessage(szErrorString, 255);
WriteErrorInLogFile(szErrorString); //функция записи в лог (пользовательская).
pE->Delete();
return FALSE;
>
delete pE;

У класса CException и его производных имеется метод ReportError – который выводит на экран сообщение об ошибке.
Так же из этого сообщения можно просто сформировать строку, например, для вывода в log файл. Для этого есть метод GetErrorMessage();
Так же MFC исключения можно ловить дедовским способом try/catch.

Вопрос. В Visual C++ я видел операторы try и TRY. В чём отличие и чем лучше пользоваться?

Макросы TRY/CATCH/AND_CATCH/END_CATCH/THROW/THROW_LAST тянутся из тех времен, когда компилятор C++ от MS еще не поддерживал стандартную обработку исключений. Пользоваться ли ими – это уже ваш выбор, но в свете сказанного ранее – не советую.

Вопрос. Как насчёт быстродействия кода получаемого при использовании исключений?
Быстродействие его практически не страдает, но вот объём существенно возрастает. И всё из за добавления кода «для отката».

Вопрос. Как насчёт исключений в UNIX-like системах?
В UNIX-ах при возникновении исключений система шлёт сигналы, например, при возникновении ошибки с плавающей точкой FreeBSD шлёт сигнал SIGFPE – Floating Point Exception.

Более подробную информацию по ловле исключений читайте в прикрепленном файле. (40 927 байт в zip архиве, всё написано русским по белому – кто не испугался – срочно качаем!).

Осталось неосвещенным много чего. Перечислю:

1) Exception handling в DOS, Win9x, UNIX.
2) Exception handling в компиляторах Borland (я слышал, что им не нужно изгаляться с преобразование C++ исключения в SEH, а что Borland кидает исключительно SEH исключения? – Borland не претендует на универсальность, и в данном случае это просто прекрасно!).
3) Exception handling в ИмяРек компиляторах.
4) Exception handling в OLE/COM.
5) Ещё я слышал, что появилась VEH обработка исключений. Пользовались? Я нет. Поделитесь опытом

Обработка ошибок при выделение памяти с помощью new в C++

Есть два способа определить выделена ли память оператором new .

Способ 1. Обработка исключения

Если память не выделена, то бросается исключение std::bad_alloc .

Пример обработки исключения.

Способ 2. Проверка указателя

При вызове new в качестве аргумента можно использовать константу std::nothrow . Тогда, исключение std::bad_alloc не испускается, а вместо него возвращается нулевой указатель.

C ++ — Обработка исключений

Исключением является проблема, возникающая во время выполнения программы. Исключение C ++ — это ответ на исключительное обстоятельство, которое возникает во время работы программы, например попытка деления на ноль.

Исключения обеспечивают способ передачи контроля из одной части программы в другую. Обработка исключений C ++ построена на трех ключевых словах: try, catch и throw .

  • throw — программа выдает исключение, когда возникает проблема. Это делается с использованием ключевого слова throw .
  • catch — программа выхватывает исключение с обработчиком исключений в месте в программе, где вы хотите справиться с этой проблемой. Ключевое слово catch указывает на отлов исключения.
  • try — блок try идентифицирует блок кода, для которого будут активированы определенные исключения. За ним следует один или несколько блоков catch.

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

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

Выбросы исключений

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

Ниже приведен пример исключения исключения при делении на нулевое условие:

Устранение исключений

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

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

Ниже приведен пример, который генерирует деление на ноль, и мы поймаем его в блоке catch .

Поскольку мы создаем исключение типа const char * , поэтому, поймав это исключение, мы должны использовать const char * в блоке catch . Если мы скомпилируем и запустим код выше, это приведет к следующему результату:

Стандартные исключения C ++

C ++ предоставляет список стандартных исключений, определенных в <exception> , которые мы можем использовать в наших программах. Вот небольшое описание каждого исключения, упомянутого в приведенной выше иерархии:

std::exception Исключение и родительский класс всех стандартных исключений C ++.
std::bad_alloc Это может быть брошено new
std::bad_cast Это может быть вызвано dynamic_cast .
std::bad_exception Это полезное устройство для обработки неожиданных исключений в программе на C ++.
std::bad_typeid Это может быть вызвано typeid .
std::logic_error Исключение, которое теоретически можно обнаружить, прочитав код.
std::domain_error Это исключение, возникшее при использовании математически недопустимого домена.
std::invalid_argument Это вызвано неправильными аргументами.
std::length_error Это генерируется при создании слишком большой std :: string.
std::out_of_range Это может быть вызвано методом «at», например, std :: vector и std :: bitset <> :: operator [] ().
std::runtime_error Исключение, которое теоретически невозможно обнаружить, прочитав код.
std::overflow_error Это бросается, если происходит математическое переполнение.
std::range_error Это происходит, когда вы пытаетесь сохранить значение, выходящее за пределы допустимого диапазона.
std::underflow_error Это бросается, если происходит математическое недополнение.

Определение новых исключений

Вы можете определить свои собственные исключения, наследуя и переопределяя функциональные возможности класса исключений . Ниже приведен пример, который показывает, как вы можете использовать класс std :: exception для реализации своего собственного исключения стандартным способом —

Это приведет к следующему результату —

Здесь what() — это открытый метод, предоставляемый классом исключений, и он был переопределен всеми дочерними классами исключений. Это возвращает причину исключения.

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *