参考:《Effective C++》
1.内存分配失败时调用new_handler
关于new_handler参考:
http://blog.csdn.net/sanoseiichirou/article/details/49945791
2.处理0内存的情况(一种处理方案是将0字节内存请求当作1字节来分配)
演示
#include
#include
void *operator new(size_t size){
std::cout << "Operator new called" << std::endl;
if (size == 0)size = 1;//处理0字节情况
void *pmem(nullptr);//保存分配好的内存首地址
std::new_handler handler;//保存new_handler
while (true) {
pmem = malloc(size);
if (pmem)return pmem;
//下面的两句由于获取new_heandler,因为没有直接的方法可以获取new_handler
//当然这里针对的是单线程的情况,如果是多线程,就需要LOCK了
handler = std::set_new_handler(nullptr);
std::set_new_handler(handler);
//如果new_handler不为空,就调用它,如果为空,直接抛出异常
if (handler)handler();
else throw(std::bad_alloc());
};
}
void OutOfMem() {
std::cout << "Run out of Mem" << std::endl;
abort();
}
int main() {
std::set_new_handler(OutOfMem);
int *p(new int(243));
return 0;
}
从上面的代码我们观察到:只有几种方法可以终结new_handler的调用:
1.new分配内存成功并返回
2.new_handler为空,直接抛出std::bad_alloc异常
3.new_handler所指向的函数中包含abort()或exit()等直接终止程序运行的函数。
4.其他……
*重载new和delete的一个重要原因是要优化某个对象的内存分配。为了安全,我们需要检测分配的大小是否正确。
要注意的是:重载的局部new和delete都是为本类服务的,而不是本类的派生类
如:
#include
#include
class base{
public:
base():dat(100){}
void *operator new(size_t size){
std::cout<<"base operator new"<return malloc(size);
}
private:
int dat;
};
class derived:public base{
public:
derived():datx(200){}
private:
int datx;
};
int main(){
derived *p(new derived);
return 0;
}
如图:此时调用的是基类的operator new
但是我们都知道,一般派生类比基类要大。
解决方法
#include
#include
#include
class base{
public:
base():dat(0){}
void *operator new(std::size_t size){
std::cout<<"base operator new called - size="<std::endl;//调试用的语句
if(size!=sizeof(base)){ //如果调用new的是派生类,则跳转到全局的new
std::cout<<"turn to global new for help"<<std::endl;//调试用的语句
return ::operator new(size);
}
return malloc(size);
}
void operator delete(void *p,std::size_t size){
std::cout<<"base operator delete called - size="<std::endl;//调试用的语句
if(p){ //检测p是否为nullptr,遵守C++释放空指针永远不报错的规则
if(size!=sizeof(base)){ //如果调用new的是派生类,则跳转到全局的delete
std::cout<<"turn to global delete for help"<<std::endl;//调试用的语句
return ::operator delete(p);
}
delete p;
}
}
private:
int dat;
};
class myclass:public base{
public:
myclass():datx(0){}
private:
int datx;
};
int main(){
base *p(new base);
delete p;
std::cout<<'\n';
myclass *px(new myclass);
delete px;
return 0;
}
当然,delete同样也要检测,若大小不符,就要交给global delete来处理。
疑问:为什么我们这里不用检测分配0字节内存的情况呢?
看个例子:
现在知道为啥不用检测分配0字节的情况了吧。
*array new也就是类似void *operator new[](size_t size)的new版本,做法就是直接分配size大小一块内存即可。
理由是:我们无法确定元素的大小和要分配的个数。(如:特殊规则1 中讲述的,对于派生类,我们无法使用基类的operator new为其分配内存;其次,new常常会多分配出一些空间来存储额外的信息)
#include
#include
#include
class demo{
public:
demo():dat(0){
std::cout<<"demo constructor called"<<std::endl;
}
~demo(){
std::cout<<"demo destructor called"<<std::endl;
}
void *operator new(std::size_t size){
if(size!=sizeof(demo))return ::operator new(size);//如果分配的大小不符,转交给全局new处理
void *p(nullptr);
std::new_handler handler(std::set_new_handler(nullptr));
std::set_new_handler(handler);
while(true){
if(p=malloc(size))return p;
if(handler)handler();
else throw std::bad_alloc();
}
}
void operator delete(void *p,std::size_t size){
if(p){
if(size!=sizeof(demo))return ::operator delete(p);//如果大小不符,转交给全局delete处理
free(p);
}
}
void *operator new[](std::size_t size){
//直接将责任交给demo中的operator new
//而operator new直接mallo返回一片raw memory(没有经过任何操作的一块内存)
return operator new(size);
//当然也可以直接malloc
//return malloc(size);
}
void operator delete[](void *p){
if(p)free(p);
}
private:
int dat;
};
int main(){
demo *p=new demo;
delete p;
std::cout<<"\n\n";
demo *px(new demo[3]);
delete [] px;
return 0;
}
*删除nullptr是安全的
#include
#include
class demo{
public:
demo():dat(100){}
void *operator new(size_t size)throw(std::bad_alloc){
return malloc(size);
}
void operator delete(void *p)throw(){
if(p!=0)delete p;
}
private:
int dat;
};
int main(){
return 0;
}
#include
#include
class demo{
public:
demo(){std::cout<<"Default Constructor"<<std::endl;}
demo(int dat):m_dat(dat){std::cout<<"Unary Constructor"<<std::endl;}
~demo(){std::cout<<"Default Destructor"<<std::endl;}
void * operator new(size_t size)throw(std::bad_alloc){
std::cout<<"operator new"<<std::endl;
if(size!=sizeof(demo))return ::operator new(size);
void *pmem(nullptr);
std::new_handler handler(nullptr);
while(true){
if(pmem=malloc(size))return pmem;
handler=std::set_new_handler(nullptr);
std::set_new_handler(handler);
if(handler)handler();
else throw(std::bad_alloc());
}
}
void *operator new[](size_t size){
std::cout<<"operator new[]"<<std::endl;
return operator new(size);
}
void operator delete(void *p){
std::cout<<"operator delete"<<std::endl;
if(p)free(p);
}
void operator delete[](void *p){
std::cout<<"operator delete[]"<<std::endl;
operator delete(p);
}
private:
int m_dat=0;
};
int main(){
demo *p(new demo);
delete p;
std::cout<<"\n\n";
p=new demo[3];
delete []p;
return 0;
}
刚才已经说过,我们没有方法直接获取new_handler,没错,但是,在VC++的最新库中已经包含有 get_new_handler 。位于< new > 头文件中。
看图:
下面是实例:
#include
#include
int main() {
std::new_handler h(std::get_new_handler());
std::cout << h << std::endl;
std::cin.get();
return 0;
}