本来想自己总结下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++