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

Как бороться с bad_alloc в С++?

Существует метод под названием foo, который иногда возвращает следующую ошибку:

terminate called after throwing an instance of 'std::bad_alloc'
  what():  std::bad_alloc
Abort

Есть ли способ, которым я могу использовать блок try - catch, чтобы остановить эту ошибку от завершения моей программы (все, что я хочу сделать, это return -1)?

Если да, то для чего это синтаксис?

Как еще я могу справиться с bad_alloc в С++?

4b9b3361

Ответ 1

Вы можете поймать его как любое другое исключение:

try {
  foo();
}
catch (const std::bad_alloc&) {
  return -1;
}

Совершенно то, что вы можете с пользой сделать с этой точки зрения, зависит от вас, но это определенно возможно технически.

Ответ 2

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

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

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

В некоторых случаях программа может попытаться освободить некоторую память и повторить попытку или использовать вторичную память (= диск) вместо ОЗУ, но эти возможности существуют только в очень специфических сценариях.

Ответ 3

Какое стандартное поведение С++ указано в new в С++?

Обычное понятие состоит в том, что если оператор new не может выделять динамическую память запрошенного размера, тогда он должен выдать исключение типа std::bad_alloc.
Тем не менее, что-то еще происходит еще до того, как выбрано исключение bad_alloc:

С++ 03 Раздел 3.7.4.1.3: говорит

Функция распределения, которая не может выделить хранилище, может вызывать установленный в настоящий момент new_handler (18.4.2.2), если таковой имеется. [Примечание. Функция распределения, предоставленная программой, может получить адрес текущего установленного пользователя new_handler с помощью функции set_new_handler (18.4.2.3).] Если функция распределения, объявленная с пустой спецификацией исключения (15.4), throw(), не может выделить хранилище, он должен вернуть нулевой указатель. Любая другая функция распределения, которая не может выделить хранилище, должна указывать только на отказ путем выброса исключения класса std:: bad_alloc (18.4.2.1) или класса, полученного из std:: bad_alloc.

Рассмотрим следующий пример кода:

#include <iostream>
#include <cstdlib>

// function to call if operator new can't allocate enough memory or error arises
void outOfMemHandler()
{
    std::cerr << "Unable to satisfy request for memory\n";

    std::abort();
}

int main()
{
    //set the new_handler
    std::set_new_handler(outOfMemHandler);

    //Request huge memory size, that will cause ::operator new to fail
    int *pBigDataArray = new int[100000000L];

    return 0;
}

В приведенном выше примере operator new (скорее всего) не сможет выделить место для 100 000 000 целых чисел, и будет вызвана функция outOfMemHandler(), и программа будет отменена после выдача сообщения об ошибке

Как показано здесь, поведение оператора new по умолчанию, когда он не может выполнить запрос на память, заключается в вызове функции new-handler, пока он не сможет найти достаточное количество памяти или новых обработчиков. В приведенном выше примере, если мы не назовем std::abort(), outOfMemHandler() будет вызываться повторно. Поэтому обработчик должен либо гарантировать, что следующее распределение будет успешным, либо зарегистрировать другой обработчик, либо не зарегистрировать обработчик, либо не вернуть (т.е. Завершить программу). Если нового обработчика нет, и выделение не выполняется, оператор выдает исключение.

Что такое new_handler и set_new_handler?

new_handler является typedef для указателя на функцию, которая принимает и ничего не возвращает, а set_new_handler - это функция, которая принимает и возвращает new_handler.

Что-то вроде:

typedef void (*new_handler)();
new_handler set_new_handler(new_handler p) throw();

set_new_handler - указатель на оператор функции new должен вызывать, если он не может выделить запрошенную память. Его возвращаемое значение является указателем на ранее зарегистрированную функцию обработчика или null, если предыдущий обработчик не был.

Как справиться с условиями памяти в С++?

Учитывая поведение new, хорошо спроектированная пользовательская программа должна обрабатывать из памяти условия, предоставляя правильный new_handler, который выполняет одно из следующих действий:

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

Установить новый новый обработчик:. Если текущий новый обработчик не может сделать больше доступной памяти, и есть другой новый обработчик, который может, то текущий новый обработчик может установите другой новый обработчик на свое место (вызывая set_new_handler). В следующий раз оператор new вызовет функцию нового обработчика, он получит тот, который был установлен последним.

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

Удалить новый обработчик: Это делается путем передачи нулевого указателя на set_new_handler. Если новый обработчик не установлен, operator new выдаст исключение ((конвертируется в) std::bad_alloc), когда распределение памяти не будет выполнено.

Отбросить исключение, конвертируемое в std::bad_alloc. Такие исключения не попадают на operator new, но будут распространяться на сайт, отправляющий запрос на память.

Не вернуть:. Вызовите abort или exit.

Ответ 4

Я бы не предложил этого, так как bad_alloc означает, что вы потеряли память. Было бы лучше просто отказаться, а не пытаться восстановиться. Однако это решение, о котором вы просите:

try {
    foo();
} catch ( const std::bad_alloc& e ) {
    return -1;
}

Ответ 5

Я могу предложить для этого более простое (и даже более быстрое) решение. Оператор new возвращает null, если память не может быть выделена.

int fv() {
    T* p = new (std::nothrow) T[1000000];
    if (!p) return -1;
    do_something(p);
    delete p;
    return 0;
}

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

Ответ 6

Пусть ваша программа foo exit контролируется:

#include <stdlib.h>     /* exit, EXIT_FAILURE */

try {
    foo();
} catch (const std::bad_alloc&) {
    exit(EXIT_FAILURE);
}

Затем напишите программу shell, которая вызывает фактическую программу. Поскольку адресные пространства разделены, состояние вашей программы оболочки всегда четко определено.