本来想自己总结下operator new相关的,看到周星星同学已经总结得挺全了,直接拿来记录下(后面绿色部分新加的)。
转自
http://blog.vckbase.com/bruceteen/archive/2009/05/27/37427.html

1:
C++标准说:An allocation function shall be a class member function or a global function; a program is ill-formed if an allocation function is declared in a namespace scope other than global scope or declared static in global scope.
必须是全局函数或类成员函数,而不能是全局之外的名字空间或static全局函数。

2:new operator的行为
Foo* p = new Foo;

...
delete p;
我们知道,上面的代码,也就是C++中的new操作符(new operator)大致会做下面的事情:
a.调用operator new, 给对象分配内存
b.调用Foo的构造函数
c.返回指针
...
d. 调用Foo的析构函数~Foo()
e. 调用operator delete释放内存

更具体的, new operator的行为如下:
对于如下代码:
    Foo* p = new(arg1,arg2,…… ) Foo(para1, para2, ...);
    ...
    delete p;
编译器将生成如下代码:
    调用 p = operator new( size_t 需要的大小,arg1,arg2,…… );  // 分配内存,这里有可能抛出std::bad_alloc,但无须在new operator中捕捉
    如果构造Foo没有抛出异常                                                    // 即Foo的构造函数后面显式的声明了 throw()
        在p指向处构造foo(para1,para2,……);                              // 调用Foo的构造函数
        return p;
    否则
        try
        {
            在p指向处构造Foo(para1,para2,……);
            return p;
        }
        catch(...)
        {
            调用 operator delete( void* p, arg1,arg2,…… );
            throw;
        }
    ...
    调用 Foo的析构函数~Foo();
    调用 operator delete( void* p );

从上面的步骤可以看出:
(1)对于operator new, 我们只要确保第一参数是表示申请内存的大小, 其他参数可以自己随意重载
(2)只有Foo构造失败(构造函数内抛出异常),我们的operator delete( void* p, arg1,arg2,…… )才会被调用,否则只会调用operator delete( void* p )

3:全局形式的operator new伪代码
void* operator new( size_t size ) // 包括其他形式
{
    if( 0 == size ) // 须要注意
        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();
    }
}
void operator delete( void* raw )
{
    if( 0 == raw ) // 须要注意
        return;
    ...
}
须要说明的是,编译器本身就隐含着一个 void* operator new( size_t ),所以重载全局operator new必须加其他参数以示区别。
一般重载分配函数时都会重载三个,分别是 void* operator new( size_t, …… ),void operator delete( void*, …… ),以及一般形式的 void operator delete( void* )。

4. set_new_handler的作用
    set_new_handler设置一个函数,此函数将在分配内存失败时被调用,见3中的代码。
    从3中的代码还能看得出,new_handler必须有主动退出的功能,否则就会导致operator new内部死循环。因此newhandler的一般形式是:
    void mynewhandler()
    {
        if( 有可能使得operator new成功(比如释放部分内存) )
        {
            做有可能使得operator new成功的事
            return;
        }
        // 主动退出
        或 abort/exit 直接退出程序
        或 set_new_handler(其他newhandler);
        或 set_new_handler(0)
        或 throw bad_alloc()或派生类 // 这一种比较好,不粗鲁的关闭程序,也不更改其他设置
    }
须要说明的是,没有类形式的set_new_handler,但这也无所谓,你可以自己写。(见《Effective C++ 2e》条款7)

5. 类形式的operator new伪代码:
struct base
{
    ...
    static void* operator new( size_t size );
    static void operator delete( void* raw );
};
void* base::operator new( size_t size )
{
    if( sizeof(base) != size ) // 须要注意
        return ::operator new(size);

    类似于3 // 注意“没有类形式的set_new_handler”
}
void base::operator delete( void* raw )
{
    if( sizeof(base) != size ) // 须要注意
    {
        ::operator delete(raw);
        return;
    }
    同3
}

6. operator new的函数类型:
对我们来说一般有3种是语言要求的标准operator new(plain new, nothrow new, placement new):
void *operator new(std::size_t count) throw(std::bad_alloc);             //一般的版本(plain new)
void *operator new(std::size_t count,  const std::nothrow_t&) throw();    //兼容早版本, new内存分配失败不会抛出异常(nothrow new)
void *operator new(std::size_t count, void *ptr) throw();  //placement版本(placement new)

上面的方法我们可以这样调用:
Foo* p = new Foo;
delete p;

Foo* p1 = new(std::nothrow) Foo;
delete p1;

Foo f;
Foo* p2 = new(&f) Foo;
p2->~Foo();

针对数组则是:
void *operator new[](std::size_t count) throw(std::bad_alloc);            
void *operator new[](std::size_t count,  const std::nothrow_t&) throw();    
void *operator new[](std::size_t count, void *ptr) throw();  

可以看到上面函数第一个都是对象空间大小,除了重载C++中operator new的标准类型,另外我们也可以重载其他类型的operator new, 比如
void *operator new(std::size_t count, const string& s) throw(std::bad_alloc);  
void *operator new[](std::size_t count, const string& s) throw(std::bad_alloc); 

然后就可以这样调用了: string str("abc"); Foo* p = new(str) Foo;

当然,如果我们自己重写了operator new, 最好我们也重写operator delete,这样如果我们的构造函数里抛出异常,我们自己重写的operator delete会被调用。(当然,如果构造对象成功,最后delete时只会调用operator delete( void* p ))

比如针对上面新加的operator new函数,新加operator delete如下:
void operator delete(void* p, const string& s) throw();
void operator delete[](void* p, const string& s) throw();
可以看到,自己新加的operator delete, 只需确保第一个参数内存指针。

7. new operator和operator new的区别

      new operator就象sizeof一样是语言内置的,我们不能改变它的含义,它的功能总是一样的。它要完成的功能分成两部分。第一部分是分配足够的内存以便容纳所需类型的对象。第二部分是它调用构造函数初始化内存中的对象。new operator总是做这两件事情,你不能以任何方式改变它的行为。

  我们所能改变的是如何为对象分配内存。new operator调用一个函数来完成必需的内存分配,你能够重写或重载这个函数来改变它的行为。new operator为分配内存所调用函数的名字是operator new。

      如果想在堆上建立一个对象,应该用new operator。它既分配内存又为对象调用构造函数。如果你仅仅想分配内存,就应该调用operator new函数;它不会调用构造函数。如果你想定制自己的在堆对象被建立时的内存分配过程,你应该写你自己的operator new函数,然后使用new operator,new operator会调用你定制的operator new。如果你想在一块已经获得指针的内存里建立一个对象,应该用placement new。
      与new operator/operator new相对应的是delete operator/operator delete, 当我们调用delete operator时,实际上包含析构函数调用和通过operator delete释放内存2个阶段。
   我们可以单纯的通过operator new 和 operator delete来分配和释放内存:
    void *buffer = operator new(50*sizeof(char)); // 内存以容纳50个char, 没有调用构造函数
  ...
  operator delete(buffer); // 释放内存, 没有调用析构函数

8. operator new的一些原则:
a. 一般不要重写全局的operator new, 具体可以参考 不要重载全局 ::operator new
b. 如果重载了operator new, 同时提供所有版本(plain new, nothrow new, placement new)
c. 成对的提供new和delete, 即如果重载了operator new, 同时重载operator delete

 

posted on 2012-10-06 22:25 Richard Wei 阅读(2740) 评论(0)  编辑 收藏 引用 所属分类: C++

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