解析
new
平常使用的new都是new operator(new操作符),它是语言内置的,无法重载。
new的第一种写法:
//call new operator
int *p = new int(3);
调用new operator后做了两件事:
1、调用operator new分配内存。
2、调用构造函数初始化对象。
operator new声明如下:
//operator new's declaration
void * operator new(size_t size);
它可以被重载,但是第一个参数类型必须是size_t, 决定分配内存的大小。
可以调用operator new只进行内存分配:
//call operator new
void *v = operator new(4);
new的第二种写法:
//another way of calling new operator
int *i = new(v)int(5);
调用new operator后做了两件事:
1、调用placement new,这里不分配内存,只返回传入的指向内存的指针。
2、调用构造函数初始化对象。
placement new的实现可能像这样:
//placement new
void * operator new(size_t, void *location)
{
return location;
}
全局的placement new不能被重载,也就是说无法定义声明为 void* operator new(size_t, void* ) 的函数。
new的第三种写法:
//array
int *p = new int[4];
调用new operator后做了两件事:
1、调用operator new[],分配内存。
2、对数组里的每一个对象调用构造函数。
可重载,声明如下:
//operator new[]'s declaration
void* operator new[](size_t);
delete:
平常使用的delete是delete operator(delete操作符),不能被重载。
delete的第一种写法:
//delete operator
delete p;
调用delete operator时做了如下两件事:
1、调用析构函数。
2、调用operator delete释放内存。
operator delete声明如下:
//operator delete's declaration
void operator delete(void *p)
operator delete只释放内存,不调用析构函数,可以被重载,可以像这样调用:
//call operator delete
operator delete(p);
delete的第二种写法:
//array
delete[] p;
调用delete operator时做了如下两件事:
1、对数组里的每个对象调用析构函数。
2、调用operator delete[]释放内存。
可重载,声明如下:
//operator delete[]'s declaration
void operator delete[](void* p)
内存分配失败时的情况
使用new分配内存失败时会抛出异常std::bad_alloc,所以如下if条件不会满足:
int *p = new int;
if(NULL == p) //此条件不会满足
若用这种方式,则内存分配失败时不会抛出异常,返回空指针:
int *p = new(nothrow) int; //类似placement new的new operator调用
if(NULL == p) //此条件可能满足
set_new_handler可以用来设置operator new分配内存出错时所调用的错误处理函数,它在<new>中声明如下:
typedef void (* new_handler) ();
new_handler set_new_handler(new_handler) throw ();
new_handler是指向错误处理函数的函数指针。
当用operator new分配内存失败时,首先调用set_new_handler设置的错误处理函数,然后抛出std::bad_alloc异常。
所以可以通过重载operator new和set_new_handler,针对某一个类,设置特定的内存分配错误时的处理,而不影响全局的new_handler,实现如下:
class Allocate
{
public:
//重载set_new_handler,设置自己的内存分配错误处理函数
static new_handler set_new_handler(new_handler p);
//重载operator new,在异常抛出前或者成功返回前恢复全局的new_handler
static void* operator new(size_t size);
private:
//保存自己的内存分配错误处理函数
static new_handler current_handler;
};
//初始化类的static数据成员
new_handler Allocate::current_handler = NULL;
//重载set_new_handler
new_handler Allocate::set_new_handler(new_handler p)
{
//保存当前传入的new_handler,返回之前的new_handler
new_handler old_handler = current_handler;
current_handler = p;
return old_handler;
}
//重载operator new
void* Allocate::operator new(size_t size)
{
//保存全局的new_handler,传入当前的new_handler
new_handler globe_handler = std::set_new_handler(current_handler);
void *memory = NULL;
try
{
//调用全局的operator new分配内存
memory = ::operator new(size);
}
catch(std::bad_alloc)
{
//分配内存失败,抛出异常前已经调用了前面设置的new_handler
//现在恢复之前的全局new_handler,并rethrow异常
std::set_new_handler(globe_handler);
throw;
}
//内存分配成功,恢复全局的new_handler
std::set_new_handler(globe_handler);
return memory;
}
重载set_new_handler使别人可以为这个类设置内存分配出错的处理函数。
重载operator new先用此类的new_handler替换了全局的new_handler,保证operator new分配内存失败时会调用自己的错误处理函数,
在离开前恢复全局的new_handler,保证其它类型的operator new失败时还会使用全局的new_handler.
使用如下:
//为Allocate类设置内存分配出错处理函数
Allocate::set_new_handler(NoMemory);
//new
Allocate *p = new Allocate;
delete p;
重载operator new和operator delete的注意事项
new:
1、如果成功返回指向内存的指针,如果失败抛出std::bad_alloc异常。
2、c++标准要求,在请求0字节分配时也要返回合法指针。
3、operator new中存在一个无限循环,以下几种情况可以跳出循环:
分配内存成功,return指向内存的指针;
外界通过set_new_handler(NULL)卸载了new_handler,导致抛出std::bad_alloc异常。
否则就会一直循环,执行new_handler。
4、注意父类的operator new可能会被子类调用,子类的size可能会比父类大。
实现伪代码:
void * operator new(size_t size)
{
if (size == 0)
{
// 处理0字节请求时,把它当作1个字节请求来处理
size = 1;
}
while (1)
{
分配size字节内存;
if (分配成功)
return (指向内存的指针);
// 分配不成功,找出当前出错处理函数
new_handler globalhandler = set_new_handler(0);
set_new_handler(globalhandler);
if (globalhandler) (*globalhandler)();
else throw std::bad_alloc();
}
}
delete:
为了保证删除空指针是安全的,需要判断传入指针是否为空。
重载operator的规则:
1、operator new的返回值是void*,第一个参数是size_t,格式:
void* operator new(size_t, para2, para3)
调用时格式:
void *p = new(para2, para3)TYPE;
2、operator delete的返回值为void,第一个参数是void*,格式:
void operator delete(void*, para2, para3)
调用时格式:
delete p //调用void operator delete(void*)
operator delete(p, para2, para3) //调用void operator delete(void*, para2, para3)
全局的重载
void* operator new(size_t) {cout<<"new(size_t)"<<endl; return NULL;}
void* operator new(size_t, int) {cout<<"new(size_t, int)"<<endl; return NULL;}
//无法重载全局的placement new,报错
//void* operator new(size_t, void*) {cout<<"new(size_t, void*)"<<endl; return NULL;}
void* operator new[] (size_t) {cout<<"new[] (size_t)"<<endl; return NULL;}
void* operator new[] (size_t, int) {cout<<"new[] (size_t, int)"<<endl; return NULL;}
//无法重载全局的placement new,报错
//void* operator new[] (size_t, void*) {cout<<"new(size_t, void*)"<<endl; return NULL;}
//operator delete
void operator delete(void*) {cout<<"delete(void*)"<<endl;}
void operator delete(void*, size_t) {cout<<"delete(void*, size_t)"<<endl;}
void operator delete[](void*) {cout<<"delete[](void*)"<<endl;}
void operator delete[](void*, size_t) {cout<<"delete(void*, size_t)"<<endl;}
调用:
int *p = new int; //call operator new(size_t)
int *p1 = new(2)int; //call operator new(size_t, int)
delete p; //call operator delete(void*)
int *p2 = new int[3]; //call operator new[] (size_t)
int *p3 = new(3) int[3]; //call operator new[] (size_t, int)
delete[] p2; //call delete[](void*)
若没有重载operator new(size_t) 和operator delete(void*) ,则调用全局的。
传说void operator delete(void*, size_t) 会在catch到异常时被调到。
[]格式同上。
类中的重载
class Object
{
public:
//operator new
void* operator new(size_t) {cout<<"new(size_t)"<<endl; return NULL;}
void* operator new(size_t, void*) {cout<<"new(size_t, void*)"<<endl; return NULL;}
//operator delete
void operator delete(void*) {cout<<"delete(void*)"<<endl;}
void operator delete(void*, size_t) {cout<<"delete(void*, size_t)"<<endl;}
};
调用:
Object *p = new Object; //call operator new(size_t)
Object *p1 = new(NULL) Object; //call operator new(size_t, void*)
delete p; //call delete(void*)
若类中没有定义
void operator delete(void*),则delete p调用void operator delete(void*, size_t)
若类中没有定义void* operator new(size_t) ,则 Object *p = new Object 报错,重载的非单参数的operator new隐藏了全局的单参数的operator new。
[]格式同上。
所以类中最好重载一下标准形式的operator new:
class Overload
{
public:
//重载operator new
static void* operator new(size_t size, int i, char c){return NULL;}
static void* operator new(size_t size){return ::operator new(size);}
};