Вопрос

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

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

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

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

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

Это было полезно?

Решение

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

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

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

Другие советы

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

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

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

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

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

Крайне редко приложения могут контролировать точку 1 — приложения пользовательского пространства. никогда сделать, это общесистемный параметр, для изменения которого требуются права root.1

Хорошо, давайте предположим, что вы зафиксировали точку 1.Теперь вы можете, например, использовать LRU-кэш для некоторых ваших данных (вероятно, для некоторых особенно крупных бизнес-объектов, которые можно регенерировать или перезагружать по требованию).Затем вам нужно поместить реальную логику, которая может привести к сбою, в функцию, поддерживающую повтор — другими словами, если она прервется, вы можете просто перезапустить ее:

lru_cache<widget> widget_cache;

double perform_operation(int widget_id) {
    std::optional<widget> maybe_widget = widget_cache.find_by_id(widget_id);
    if (not maybe_widget) {
        maybe_widget = widget_cache.store(widget_id, load_widget_from_disk(widget_id));
    }
    return maybe_widget->frobnicate();
}

…

for (int num_attempts = 0; num_attempts < MAX_NUM_ATTEMPTS; ++num_attempts) {
    try {
        return perform_operation(widget_id);
    } catch (std::bad_alloc const&) {
        if (widget_cache.empty()) throw; // memory error elsewhere.
        widget_cache.remove_oldest();
    }
}

// Handle too many failed attempts here.

Но даже здесь, используя std::set_new_handler вместо того, чтобы обращаться std::bad_alloc обеспечивает ту же выгоду и будет намного проще.


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

Каково поведение, указанное в стандарте C++? 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 является определением типа для указателя на функцию, которая ничего не принимает и не возвращает, и 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, он получит ту, которая была установлена ​​последней.

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

Удалите новый обработчик: Это делается путем передачи нулевого указателя 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 вернет нуль, если память не может быть выделена.

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

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

Пусть ваша foo program Выход в контролируемомпуть:

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

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

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

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top