Как бороться с плохим аллоком в C++?

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

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

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

если да, то каков его синтаксис?

как еще я могу иметь дело с bad_alloc в C++?

6 ответов


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

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

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


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

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

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

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


каково стандартное поведение c++new в c++?

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

C++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 оператор когда неспособный выполнить запрос памяти, вызвать the new-handler функция повторно, пока она не сможет найти достаточно памяти или нет больше новых обработчиков. В приведенном выше примере, если мы называем std::abort(), outOfMemHandler() будет неоднократно звонил. Поэтому обработчик должен либо убедиться, что следующее распределение выполнено успешно, либо зарегистрировать другой обработчик, либо не зарегистрировать обработчик, либо не возвращать (т. е. завершить программу). Если нет нового обработчика и распределение завершается неудачно, оператор выдаст исключение.

что the 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, если не было предыдущий обработчик.

как обрабатывать из условий памяти в C++?

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

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

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

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

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

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

не вернуть: по телефону abort или exit.


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

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

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

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

Я надеюсь, что это может помочь!


пусть ваш программа foo выход контролируемым образом:

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

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

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