new operator /operator new 跟placement new
本文介绍这三种new/delete之间的区别和联系。
new operator
new operator(new运算符)是我们C++中常用的运算符,比如A* a = new A;创建一个A对象指针。
new operator分为两步:
1.基于类型的size调用operator new分配一块内存。如果A中重载了operator new,就调用A::operator new(size_t),否则调用全局默认C++自己提供的::operator new(size_t)
2.构造对象。
operator new
C++默认提供一个operator new函数,它是一种通用的内存分配器,可以分配任意大小的内存块。
比如T * tmp = (T*)(::operator new(size_t)size);
创建一个大小为size的内存块,指针tmp指向这块内存块。
operator delete也要可以释放任意大小的内存块。operator delete想弄清它要释放的内存有多大,就必须知道当初operator new分配的内存有多大。有一种常用的方法可以让operator new来告诉operator delete当初分配的内存大小是多少,就是在它所返回的内存里预先附带一些额外信息,用来指明被分配的内存块的大小。也就是说,当你写了下面的语句,
airplane *pa = new airplane;
你不会得到一块看起来象这样的内存块:
pa——> airplane对象的内存
而是得到象这样的内存块:
pa——> 内存块大小数据 + airplane对象的内存
重写operaotr new
operator new的工作是分配内存,为什么要重写:为了效率。有些类占用内存非常小,每次创建都分配内存太慢,可以先分配一块大的内存池,然后每次在内存池里面直接构造对象。
比如有一个飞机类:
class airplanerep { ... }; // 表示一个飞机对象
class airplane {
public:
...
private:
airplanerep *rep; // 指向实际描述
};
一个airplane对象并不大,它只包含一个指针,当调用operator new来分配一个airplane对象时,得到的内存可能要比存储这个指针(或一对指针)所需要的要多。为什么,我们之前介绍了,C++提供的默认operator new分配的内存需要记录内存的size。
通过重写operator new,可以设计特有的分配内存方式。比如http://www.kuqin.com/effectivec2e/ch02e.htm中创建airplane时首先创建一大块内存,并且使用链表的方式控制未使用的内存区域。每次new的时候,从链表中选择一个节点作为分配的内存。
重写了operator new以后,当使用new operator构建一个对象的时候,会自动去调用operator new函数,将返回的指针作为分配好的指针。
在自己重写operator new分配的大内存上创建的对象,不能使用delete直接把内存删除。
问题出在operator new(在airplane里定义的那个)返回了一个不带头信息的内存的指针,而operator delete(缺省的那个)却假设传给它的内存包含头信息。这就是悲剧产生的原因。
placement new
placement new是在已经分配好的内存上,构造一个对象。如new(p)T(value)
,在p指向的已分配好的内存首地址上,使用构造函数T(value)构造一个新的T对象。
讨论
new operator没有什么好说的,就是我们平时使用的new操作符,他会请求分配一块内存然后构造对象。
当我们需要自己控制内存申请机制的时候,比如我们希望创建一个内存池,然后在这块内存上构造对象,有两种方法:
1.重写operator new
如http://www.kuqin.com/effectivec2e/ch02e.htm中举的例子。为类重写了operator new以后,类构造对象的时候,自动去调用重写的operator new分配内存。
2.使用placement new
首先申请一块内存(可以使用默认的operator new),然后在这块内存上创建对象。
T* tmp = (T*)(::operator new((size_t)(size * sizeof(T))));//申请内存
new (p)T1(value)//构造函数