#опытным
Дело в том, что 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