set_new_handler

本文探讨了C++中使用nothrow_t版和throw(std::bad_alloc)版的operator new进行内存分配的方法,并介绍了如何通过set_new_handler处理内存分配失败的情况。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

之前,曾对new做了一些总结,详见" new的理解".
在看《effective C++》第8章时,对内存分配有一些新的认识,这里主要讲一讲对set_new_handler的理解。

当使用nothrow_t版的operator new时,如果不能满足一个内存分配请求,会返回一个NULL指针。
void* operator new (std::size_t size, const std::nothrow_t& nothrow_value) throw()
当使用throw (std::bad_alloc)版的operator new时,如果不能满足一个内存分配请求,会抛出一个异常。
void* operator new (std::size_t size) throw (std::bad_alloc)

在 operator new 因一个无法满足的内存请求而抛出一个异常之前,它先调用一个可以由客户指定的被称为 new-handler 的错误处理函数。
为了指定这个错误处理函数,客户需先设置 set_new_handler函数,内存分配失败后会反复调用这个new-handler 的错误处理函数。

错误处理函数的原型:
typedef void (*new_handler)();  //一个函数指针

set_new_handler的原型:
new_handler currentHandler = 0;
new_handler set_new_handler(new_handler p)  //设置当前new_handler,返回前一个new_handler
{
  new_handler oldHandler = currentHandler;
  currentHandler = p;
  return oldHandler;
}

我在下例中,做了2个new-handler:
void newHandlerFree(); //释放一些内存,确保下次能够分配内存

void outOfMemHandler();  //没有内存了,set_new_handler(0),不再反复调用new-handler


#include <iostream>
using namespace std;

//buffer size
const int SIZE=99999999;
char *buffer = NULL;

//placement new
//void* operator new (std::size_t size, void* ptr) throw();

//重载nothrow new: int* p = new(std::nothrow) int[100000000ul] 
//struct nothrow_t {}; 一个空结构体,为了区分throw new.
//void* operator new (std::size_t size, const std::nothrow_t& nothrow_value) throw()


//重载throw new
void* operator new (std::size_t size) throw (std::bad_alloc)
{    
	if (size == 0)
	//如果内存请求大小为0,设为1
		size = 1;
	void *p_alloc = 0;
	//	while (!(p_alloc = ::new char[size])) 不能用std ::new, 依旧会调用到这里的operator new
	while (!(p_alloc = malloc(size)))
	{
        cout << "Memory allocation failed"<<endl;
		new_handler globalHandler = set_new_handler(0);
		set_new_handler(globalHandler);
		
		if (globalHandler) 
			(*globalHandler)();
		else  //不再处理 new-handler
			throw std::bad_alloc();
	}
	return p_alloc;
}

//没有内存了,set_new_handler(0),不再反复调用new-handler 
void outOfMemHandler()
{
  cout<<"Out of memory"<<endl;
  set_new_handler(0);
} 

//释放一些内存,确保下次能够分配内存  
void newHandlerFree()
{
	if(NULL != buffer)
	{
	  cout << "Free some memory(buffer)"<<endl;
  	  delete buffer;
	  buffer = NULL;
	  return;
	}
	cout << "No more memory for free"<<endl;
    set_new_handler(outOfMemHandler);
}
 
int main()
{
    //buffer将会在后续内存分配失败后释放
	//这里buffer的size随着不同的环境调整,主要是为了制造没有足够内存分配
    buffer  = new char[SIZE*4999];

    set_new_handler(newHandlerFree);
    try 
	{
        while (true) 
		{
            new char[SIZE*4999];
			cout << "Memory allocation successful"<<endl;
        }
    } 
	catch (const std::bad_alloc& e) 
	{
      cout <<"Catch exception: "<< e.what() << endl;
    }
}

输出结果:

... ...
Memory allocation successful
Memory allocation successful
Memory allocation successful
Memory allocation successful
Memory allocation successful
Memory allocation successful
Memory allocation failed
Free some memory(buffer)
Memory allocation successful
Memory allocation failed
No more memory for free
Memory allocation failed
Out of memory
Memory allocation failed
Catch exception: St9bad_alloc


<think>好的,用户现在问的是关于`set_new_handler`的作用。首先,我需要回忆这个函数的功能和相关知识。`set_new_handler`是C++标准库中的一个函数,用于设置当`new`操作符无法分配足够内存时调用的处理函数。用户可能在编写需要处理内存分配失败情况的程序时遇到这个问题,或者他们在学习异常处理或资源管理时接触到这个函数。 接下来,我需要确定用户的需求。他们可能想知道这个函数的基本作用,但可能还有更深层次的需求,比如如何正确使用它,或者在实际项目中有什么应用场景。考虑到用户之前询问过关于allocator的问题,他们可能在实现自己的内存管理工具,或者在学习STL的底层机制,这时候了解`set_new_handler`会有帮助。 然后,我需要考虑如何组织回答。首先解释函数的作用,然后给出使用示例,说明其工作原理,再讨论注意事项和实际应用场景。同时,需要注意用户可能对C++的内存分配机制不太熟悉,所以需要用简洁明了的语言,避免过于技术化的术语。 另外,用户可能对为什么需要自定义内存分配失败处理函数感到疑惑,因此需要说明默认行为(抛出`std::bad_alloc`)和自定义处理函数的用途,比如释放其他内存重试,或者记录日志等。还要提到处理函数应该完成的任务,比如释放内存或抛出异常,否则可能导致无限循环。 还需要考虑用户可能的误解,例如认为`set_new_handler`可以完全替代异常处理,但实际上它只是提供了一种机制,让程序在内存不足时有更多的控制权。同时,需要提醒用户,C++11之后有`nothrow`版本的`new`,但`set_new_handler`仍然是处理全局内存分配失败的一种方法。 最后,总结`set_new_handler`的重要性,强调其在资源敏感型应用中的作用,并确保用户理解如何正确使用它,避免常见错误,比如处理函数中没有释放内存导致死循环,或者忘记抛出异常。</think>`set_new_handler` 是 C++ 标准库中的一个函数,用于**设置全局的内存分配失败处理函数**。当程序使用 `operator new` 或 `operator new[]` 动态分配内存失败时(例如内存不足),C++ 会调用当前设置的 `new_handler` 函数,尝试恢复或处理这一错误。 --- ### **核心作用** 1. **自定义内存分配失败的处理逻辑** 默认情况下,内存分配失败会抛出 `std::bad_alloc` 异常。通过 `set_new_handler`,可以注册一个自定义函数,在内存分配失败时执行特定操作(例如释放其他内存、记录日志、终止程序等)。 2. **支持“重试”机制** 在 `new_handler` 函数中,可以尝试释放程序中的其他内存(例如缓存),然后让 `operator new` 重新尝试分配。如果释放了足够内存,分配可能成功。 --- ### **函数原型** ```cpp #include <new> // 需要包含此头文件 // 返回旧的 new_handler,设置新的 new_handler std::new_handler set_new_handler(std::new_handler new_p) noexcept; ``` --- ### **使用示例** ```cpp #include <iostream> #include <new> // 包含 set_new_handler #include <cstdlib> // 包含 exit // 自定义内存分配失败处理函数 void MyNewHandler() { std::cerr << "Memory allocation failed! Trying to recover...\n"; // 1. 尝试释放某些预留内存(假设此处有释放逻辑) // ... // 2. 如果无法恢复,终止程序或抛出异常 std::exit(1); // 终止程序 // 或者:throw std::bad_alloc(); // 回退到默认行为(抛出异常) } int main() { // 设置自定义处理函数 std::set_new_handler(MyNewHandler); // 尝试分配超大内存(触发失败) try { int* p = new int[1000000000000L]; // 假设分配失败 } catch (const std::bad_alloc& e) { std::cerr << "Caught exception: " << e.what() << "\n"; } return 0; } ``` --- ### **关键点** 1. **处理函数的责任** - 如果 `new_handler` 函数返回(未终止程序或抛出异常),`operator new` 会**重新尝试分配内存**,形成循环。 - 因此,处理函数必须确保在返回前释放足够内存,或通过终止程序、抛出异常跳出循环。 2. **多线程安全** - `set_new_handler` 是全局设置,多线程环境下需谨慎使用(可能需加锁)。 3. **与 `nothrow` 版本的兼容性** - `new (std::nothrow)` 不会调用 `new_handler`,而是直接返回 `nullptr`。 --- ### **实际应用场景** - **内存敏感型程序**(如嵌入式系统):在内存不足时尝试释放预留的应急内存。 - **日志记录**:记录内存分配失败的事件。 - **优雅终止**:在无法恢复时,保存程序状态并安全退出。 --- ### **注意事项** - 如果未设置 `new_handler`,默认行为是抛出 `std::bad_alloc`。 - C++11 引入了 `std::get_new_handler`,用于获取当前 `new_handler`。 通过合理使用 `set_new_handler`,可以增强程序对内存分配失败场景的鲁棒性。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值