TGViewer
Грокаем C++ Грокаем C++ @grokaemcpp · 9.36K subscribers
Post #613 5.29K
Еще один способ сделать new небросающим
#опытным

Дело в том, что new - не совсем ответственнен за поведение при недостатке памяти. По плюсовой традиции тут можно кастомизировать почти все. Встречайте: std::new_handler.

Это такой typedef'чик:

typedef void (*new_handler)();


И алиас для функций, которые отвечают за обработку ситуации нехватки памяти. И вызываются они аллоцирующими функциями operator new и operator new[].

Чтобы установить такой хэдлер используется функция std::set_new_handler:


std::new_handler set_new_handler(std::new_handler new_p) noexcept;


Она делает new_p новой глобальной функцией нового обработчика и возвращает ранее установленный обработчик.

Предполагаемое назначение хэндлера - одна из трех вещей:

1️⃣ Сделать больше памяти доступной. (За гранью фантастики)

2️⃣ Залогировать проблему и завершить программу (например, вызовом std::terminate) +. Если вам плевать на graceful shutdown, то ок.

3️⃣ Кинуть исключение типа std::bad_alloc или его отпрысков с какой-нибудь кастомной надписью и/или залогировать проблему.

Раз std::new_handler - алиас на указатель функции, то что будет, если мы передадим nullptr в качестве хэндлера?

На самом деле это и делается по умолчанию. При старте программы nullptr выставляется в качестве хэндлера. При невозможности выделить память operator new вызывает std::get_new_handler. Если указатель на функцию нулевой, то в этом случае new ведет себя дефолтно - просто кидает исключение std::bad_alloc. В ином случае вызывает хэндлер.

С обработчиками есть один нюанс.

Если обработчик успешно заканчивает работу(то есть из него не вызван terminate или не брошено исключение), то operator new повторяет ранее неудачную попытку выделения и снова вызывает обработчик, если выделение снова не удается. Чтобы закончить цикл, new-handler может вызвать std::set_new_handler(nullptr): если после неудачной попытки new обнаружит, что std::get_new_handler возвращает нулевое значение указателя и выбросит std::bad_alloc.

Примерно так это работает:

void handler()
{
std::cout << "Memory allocation failed, terminating\n";
std::set_new_handler(nullptr);
}
 
int main()
{
std::set_new_handler(handler);
try
{
while (true)
{
new int [1000'000'000ul] ();
}
}
catch (const std::bad_alloc& e)
{
std::cout << e.what() << '\n';
}
}


Пользуйтесь фичей, чтобы оставлять на проде девопсерам сообщение: "А че так мало памяти в конфиге пода прописано, мм?"

Спасибо @Nikseas_314 за идею для поста)

Customize your tools. Stay cool.

#memory #cppcore
  • 👍 37
  • 😁 16
  • ❤‍🔥 6
  • ❤ 4
  • 🔥 2
More from @grokaemcpp
  1. Oct 8, 2026​​Strict weak ordering #опытным На первый взгляд, всё выглядит рабочим: мы создаём 40 зака…
  2. Oct 7, 2026​​Где-то баг... #опытным Вот вам код: struct Order { int price; int id; }; int main() { st…
  3. Oct 5, 2026Откуда spurious wakeup на кондваре? #опытным У кондваров есть метод std::condition_variabl…
  4. Oct 1, 2026​​Stacktrace. Tips #опытным Чтобы полноценно работать со стандартными трейсами, нужно знат…
  5. Sep 28, 2026​​Stacktrace #опытным Одна из проблема исключений - непонятно, откуда оно прилетело. Ну да…
  6. Sep 25, 2026​​std::spanstream #опытным Радостная весть для всех, кто пользуется iostreams! В C++23 доб…
Threads Profile ViewerView any public Threads profile without an account.Open ThreadLook →Writing with AI? Make it sound human.Metric37 rewrites AI drafts so they read naturally. Free AI detector, 1,500 words free.Try Metric37 →