c++ 中new 的 C++ 标准规定的行为是什么?
通常的想法是,如果new 运算符无法分配所请求大小的动态内存,那么它应该抛出std::bad_alloc 类型的异常。
但是,甚至在引发 bad_alloc 异常之前,还会发生更多事情:
C++03 第 3.7.4.1.3 节: 说
分配存储失败的分配函数可以调用当前安装的 new_handler(18.4.2.2),如果有的话。 [注意:程序提供的分配函数可以使用 set_new_handler 函数 (18.4.2.3) 获取当前安装的 new_handler 的地址。] 如果使用空异常规范 (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() 将被调用,程序将在 issuing an error message. 之后中止
正如这里所见,new 运算符在无法满足内存请求时的默认行为是重复调用new-handler 函数,直到它找到足够的内存或没有更多新的处理程序。在上面的示例中,除非我们调用std::abort(),否则outOfMemHandler() 将是called repeatedly。因此,处理程序应该确保下一次分配成功,或者注册另一个处理程序,或者不注册处理程序,或者不返回(即终止程序)。如果没有新的handler,分配失败,操作符会抛出异常。
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_handler执行以下操作之一:
提供更多可用内存:这可能允许 operator new 循环内的下一次内存分配尝试成功。实现这一点的一种方法是在程序启动时分配一大块内存,然后在第一次调用 new-handler 时释放它以供程序使用。
安装不同的 new-handler: 如果当前的 new-handler 不能提供更多可用内存,并且有另一个 new-handler 可以,那么当前的 new-handler 可以在其位置安装另一个新处理程序(通过调用set_new_handler)。下一次 operator new 调用 new-handler 函数时,它将获得最近安装的那个。
(这个主题的一个变体是让 new-handler 修改它自己的行为,所以下次调用它时,它会做一些不同的事情。实现这一点的一种方法是让 new-handler 修改静态的、命名空间-影响新处理程序行为的特定或全局数据。)
卸载新处理程序:这是通过将空指针传递给set_new_handler 来完成的。如果没有安装 new-handler,operator new 将在内存分配不成功时抛出异常((可转换为)std::bad_alloc)。
抛出异常可转换为std::bad_alloc。 operator new 不会捕获此类异常,但会传播到发起内存请求的站点。
不返回:通过调用abort 或exit。