当 operator new 无法满足某一内存分配需求时,它会抛出异常。旧式编译器会返回 null 指针。
而抛出异常之前,它会先调用一个客户指定的错误处理函数: new-handler
。
为了指定这个“用以处理内存不足”的函数,客户必须调用 set_new_handler
,那是声明于 的一个标准程序库函数:
namespace std {
typedef void (*new_handler)();
new_handler set_new_handler(new_handler p) throw();
} // namespace std
// Example
void OutOfMem() {
std::cerr << "Unable to satisfy request for memory" << std::endl;
std::abort();
}
int main() {
std::set_new_handler(OutOfMem);
int *pBigDataArray = new int[10000000000000000L];
pBigDataArray[0] = 213;
delete[] pBigDataArray;
pBigDataArray = nullptr;
return 0;
}
// 结果:
Unable to satisfy request for memory
Aborted (core dumped)
new-handler 是一个 typedef,定义出一个指针指向函数,该函数没有参数也不返回任何东西。
指向一个无参数值无返回值的函数。我们可以通过 set_new_handler 函数去指定客户想要的 new-handler。
set_new_handler 函数获得一个 new-handler 并返回一个new-handler 函数。
set_new_handle 声明式尾部的 throw() 是一份异常明细,表明该函数不抛出任何异常。
当 operater new 无法满足内存申请时,它会不断调用 new-handler 函数,直到找到足够内存。
一个设计良好的 new-handler 函数必须考虑以下几点:
C++ 并不支持为每一个 class 提供专属版本的 new_handler,可以为 class 提供自己实现的 set_new_handler 函数 和 operator new 函数。
class Randy {
private:
static std::new_handler currentHandler_;
public:
static std::new_handler Set_new_handler(std::new_handler p) throw();
static void* operator new(std::size_t size) throw(std::bad_alloc);
~Randy(){};
};
std::new_handler Randy::Set_new_handler(std::new_handler p) throw() {
std::new_handler oldHandler = currentHandler_;
currentHandler_ = p;
return oldHandler;
}
// Static 成员必须在class 定义式之外被定义(除非它们是const 且是整数型)
// 在class 实现文件内初始化为0
std::new_handler Randy::currentHandler_ = 0;
Randy的 operator new 做以下事情:
可以使用 RAII 管理 new_handler
class NewHandlerHolder {
public:
explicit NewHandlerHolder(std::new_handler nh) : handler(nh) {} // 取得目前的 new-handler
~NewHandlerHolder() { std::set_new_handler(handler); } // 释放 new-handler
private:
std::new_handler handler; // 记录下来
NewHandlerHolder(const NewHandlerHolder&); // 阻止 copying
NewHandlerHolder& operator=(const NewHandlerHolder&);
};
class Randy {
private:
static std::new_handler currentHandler_;
public:
static std::new_handler Set_new_handler(std::new_handler p) throw();
static void* operator new(std::size_t size) throw(std::bad_alloc) {
// 安装 Randy 的new-handler
NewHandlerHolder h(std::set_new_handler(currentHandler_));
return ::operator new(size); // 分配内存或抛出异常。
}; // 恢复 global new-handler
~Randy(){};
};
std::new_handler Randy::Set_new_handler(std::new_handler p) throw() {
std::new_handler oldHandler = currentHandler_;
currentHandler_ = p;
return oldHandler;
}
std::new_handler Randy::currentHandler_ = 0;
void OutOfMem(); // 函数声明。此函数在 Randy 对象分配失败时被调用。
int main() {
Randy::Set_new_handler(OutOfMem); // 设定 OutOfMem 为 Randy 的new-handing 函数
Randy* pr1 = new Randy; // 如果内存分配失败,调用 OutOfMem
Randy::Set_new_handler(0); // 设定 Randy 专属 的new-handling 函数为null
return 0;
}
一个更好的方式是使用 template 进行模板编程,确保每一个derived class 获得一个实体互异的currentHandler成员变量,即根据不同 class 进行特化和具现化
。完整实现如下:
template
class NewHandlerSupport {
public:
static std::new_handler set_new_handler(std::new_handler p) throw() {
const std::new_handler oldHandler = currentHandler;
currentHandler = p;
return oldHandler;
}
void* operator new(std::size_t Size) throw(std::bad_alloc) {
NewHandlerHolder(std::set_new_handler(currentHandler));
return ::operator new(Size);
}
private:
static std::new_handler currentHandler;
};
// 以下将每一个 currentHandler 初始化为 null
template
std::new_handler NewHandlerSupport::currentHandler = 0;
class Randy: public NewHandlerSupport {
... // 不必声明 set_new_handler 和 operator new
};
为什么需要 template?
你可能注意到 NewHandlerSupport template 从未使用其类型参数T。
实际上,T 的确不被需要,我们只是希望,继承自 NewHandlerSupport 的 每一个class,拥有实体互异的 NewHandlerSupport 复件(其currentHandler 成员)。
类型参数只是用来区分不同的派生类,Template 机制会自动为每一个 T 生成一份 CurrentHandler 成员。