大胖的部落格

Just a note

  C++博客 :: 首页 :: 新随笔 :: 联系 :: 聚合  :: 管理 ::
  112 随笔 :: 0 文章 :: 3 评论 :: 0 Trackbacks

解析
new
平常使用的new都是new operator(new操作符),它是语言内置的,无法重载。
new的第一种写法:

//call new operator
int *= 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 *= operator new(4);


new的第二种写法:

//another way of calling new operator
int *= 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 *= 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 *= new int;
if(NULL == p)                            //此条件不会满足
若用这种方式,则内存分配失败时不会抛出异常,返回空指针:
int *= 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 *= 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 *= 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 *= 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(3int[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 *= 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 *= 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);}
}
;
posted on 2009-05-21 11:50 大胖 阅读(1978) 评论(0)  编辑 收藏 引用 所属分类: C++

只有注册用户登录后才能发表评论。
网站导航: 博客园   IT新闻   BlogJava   知识库   博问   管理