首先我们区分下几个容易混淆的关键词:
new、operator new、placement new
new和delete操作符我们应该都用过,它们是对堆中的内存进行申请和释放,而这两个都是不能被重载的。要实现不同的内存分配行为,需要重载operator new,而不是new和delete。
operator new
(1) 只分配所要求的空间,不调用相关对象的构造函数。当无法满足所要求分配的空间时,则
->如果有new_handler,则调用new_handler,否则
->如果没要求不抛出异常(以nothrow参数表达),则执行bad_alloc异常,否则
->返回0
(2) 可以被重载
但是不能在全局对原型为void operator new(size_t size)这个原型进行重载,
一般只能在类中进行重载。如果类中没有重载operator new,
那么调用的就是全局的::operator new来完成堆的分配。
同理,operator new[]、operator delete、operator delete[]也是可以重载的。
(3) 重载时,返回类型必须声明为void*
(4) 重载时,第一个参数类型必须为表达要求分配空间的大小(字节),类型为size_t
(5) 重载时,可以带其它参数
new(也称作new operator) (1) 调用operator new分配足够的空间,并调用相关对象的构造函数 (2) 不可以被重载
1 |
|
placement new 是重载operator new的一个标准、全局的版本。它不能被自定义的版本代替(不像普通的operator new和operator delete能够被替换成用户自定义的版本)。它的原型如下:
1 |
|
void *__CRTDECL operator new(size_t size) _THROW1(_STD bad_alloc)
{
// try to allocate size bytes
void *p;
while ((p = malloc(size))
== 0)
if (_callnewh(size) == 0)
{
// report no memory
static const std::bad_alloc
nomem;
_RAISE(nomem);
}
return (p);
}
placement new,只是operator new的一个重载的版本,只是我们很少用到它。如果你想在已经分配的内存中创建一个对象,使用new时行不通的。也就是说placement new允许你在一个已经分配好的内存中(栈或者堆中)构造一个新的对象。原型中void*p实际上就是指向一个已经分配好的内存缓冲区的的首地址。
我们知道使用new操作符分配内存需要在堆中查找足够大的剩余空间,这个操作速度是很慢的,而且有可能出现无法分配内存的异常(空间不够)。placement new就可以解决这个问题。我们构造对象都是在一个预先准备好了的内存缓冲区中进行,不需要查找内存,内存分配的时间是常数。而且不会出现在程序运行中途出现内存不足的异常。所以,placement new非常适合那些对时间要求比较高,长时间运行不希望被打断的应用程序。 placement new使用方法如下:
1 |
1)分配内存 |
1 |
栈上:<br> char
buf[ sizeof (Foo) ]={0};<br>2)构建对象 |
1 |
3)使用对象 |
1 |
4)析构对象,显式的调用类的析构函数。 |
1 |
5)销毁内存 |
placement
new的好处:
1)在已分配好的内存上进行对象的构建,构建速度快。
2)已分配好的内存可以反复利用,有效的避免内存碎片问题。
Placement new的标准用法:
#include <iostream> using namespace std; class Foo { char cc; float f; public: void print() { std::cout << "ADDR: " << this << std::endl; } void set_f( float _f ) { std::cout << "set f val : " << _f << std::endl; f = _f; } void get_f() { std::cout << "get f val : " << f << std::endl; } ~Foo(){ cout <<"~Foo()"<<endl; } }; int main() { char* buff = new char[ sizeof(Foo) ]; memset( buff, 0, sizeof(Foo) ); Foo *pfoo = new (buff)Foo; pfoo->print(); pfoo->set_f( 1.0f ); pfoo->get_f(); pfoo->~Foo(); delete [] buff; return 0; }
placement new还可以解决的一个问题是建立带参数的构造函数对象数组
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29 |
#include <iostream> using
namespace std; class
CPong { public : CPong( int
m ) : v(m) { std::cout << "CPong ctor."
<< std::endl; } private : int
v; }; int
main() { char * pong = new
char [ sizeof (CPong) * 10 ]; CPong* pp = (CPong*)pong; for
( int i=0; i<10; ++i ) { new
(pp+i)CPong(i); } for
( int j=0; j<10; ++j ) { pp[j].~CPong(); } delete
[] pong; return
0; } |
注意:
在C++标准中,对于placement operator new []有如下的说明:
placement operator new[] needs implementation-defined amount of additional
storage to save a size of
array。所以我们必须申请比原始对象大小多出sizeof(int)个字节来存放对象的个数,或者说数组的大小。
使用方法第二步中的new才是使用placement
new,其实是没有申请内存的,只是调用了构造函数。返回一个指向已经分配好的内存的一个指针,所以对象销毁的时候不需要调用delete释放空间,但必须调用析构函数销毁对象。
参考自:http://www.ksarea.com/articles/20080124_cc.html
operator new分为三种形式(前2种不调用构造函数,这点区别于new operator):
1
2
3 |
1: void * operator new
(std:: size_t
size) throw
(std::bad_alloc); 2: void * operator new
(std:: size_t
size, const
std::nothrow_t& nothrow_constant) throw (); 3: void * operator new
(std:: size_t
size, void * ptr) throw (); |
第一种分配size个字节的存储空间,并进行对象类型进行内存对齐。如果成功,返回一个非空的指针指向首地址。失败抛出bad_alloc异常。
第二种在分配失败时不抛出异常,它返回一个NULL指针。
第三种是placement new版本。它不分配内存,调用合适的构造函数在ptr所指的地方构造一个对象,之后返回实参指针ptr。
三种版本的operator new 定义在全局命名空间,不在std中。第一、第二个版本C++默认在每个编译单元中声明,不需要#include <new>头文件。第一、第二个版本可以被用户更换和重载,定义自己的版本,第三种placement new不可重载。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28 |
// operator new example #include <iostream> #include <new> using
namespace std; struct
myclass {myclass() {cout << "myclass constructed\n" ;}}; int main () { int
* p1 = new
int ; // same as: // int * p1 = (int*) operator new (sizeof(int)); int
* p2 = new
( nothrow ) int ; // same as: // int * p2 = (int*) operator new (sizeof(int),nothrow); myclass * p3 = (myclass*) operator new
( sizeof (myclass)); // (!) not the same as: // myclass * p3 = new myclass; // (constructor not called by function call, even for non-POD types) new
(p3) myclass; // calls constructor // same as: // operator new (sizeof(myclass),p3) return
0; } |
重载operator new
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 |
class
Base { public : Base() { } void
*operator new ( size_t
size, string str ) { cout << "Logging an allocation of "
<< size << " bytes for new object‘"
<< str << "‘"
<< endl; return
malloc ( size ); } int
var; double
var2; }; ... Base* b = new
( "Base instance 1" ) Base; |
new operator的四种用法
1
2
3
4
5
6
7
8 |
pointer = new
type; pointer = new
type( initializer ); pointer = new
type[size]; pointer = new ( arg-list ) type... //4th vision Foo *foo; foo = new ( nothrow ) Foo(); //use the 4th vision assert ( foo );<br><span>其中, nothrow 为 C++预定义的std::nothrow_t类型全局常量。</span> |
operator new可以被重载,它的三个版本本质上是函数,它们只分配空间,不调用构造函数。而new、delete(也叫做new operator、delete operator)不可被重载,它们是运算符,分配空间,并且调用构造函数。
new、operator new、placement new,布布扣,bubuko.com
new、operator new、placement new
原文:http://www.cnblogs.com/staring-hxs/p/3624107.html