原文地址: http://www.cnblogs.com/younes/archive/2010/04/26/1721528.html 向原作者致敬!
Placement new、operator new、new operator 完全釋疑
首先我們區分下幾個容易混淆的關鍵詞:
new(也稱作new operator)、operator new、placement new。看如下代碼:
1: class MyClass {...};
2: MyClass *p = new MyClass;
這裏的new是上述三個關鍵字中的第一個,成爲new操作符。實際上它執行如下3個過程:
1. 調用operator new分配內存
2. 調用構造函數生成類對象
3. 返回相應指針
placement new 是重載operator new的一個標準、全局的版本。它不能被自定義的版本代替(不像普通的operator new和operator delete能夠被替換成用戶自定義的版本)。它的原型如下:
1
|
void *operator new ( size_t , void *p
) throw ()
{ return p;
} |
new和delete操作符我們應該都用過,它們是對堆中的內存進行申請和釋放,而這兩個都是不能被重載的。要實現不同的內存分配行爲,需要重載operator new和operator delete,而不是new和delete。
operator new就像operator+一樣,是可以重載的。但是不能在全局對原型爲void operator new(size_t size)這個原型進行重載,一般只能在類中進行重載。如果類中沒有重載operator new,那麼調用的就是全局的::operator new來完成堆的分配。同理,operator new[]、operator delete、operator delete[]也是可以重載的。
至於placement new,只是operator new的一個重載的版本,只是我們很少用到它。如果你想在已經分配的內存中創建一個對象,使用new時行不通的。也就是說placement new允許你在一個已經分配好的內存中(棧或者堆中)構造一個新的對象。原型中void*p實際上就是指向一個已經分配好的內存緩衝區的的首地址。
我們知道使用new操作符分配內存需要在堆中查找足夠大的剩餘空間,這個操作速度是很慢的,而且有可能出現無法分配內存的異常(空間不夠)。placement new就可以解決這個問題。我們構造對象都是在一個預先準備好了的內存緩衝區中進行,不需要查找內存,內存分配的時間是常數。而且不會出現在程序運行中途出現內存不足的異常。所以,placement new非常適合那些對時間要求比較高,長時間運行不希望被打斷的應用程序。 placement new使用方法如下:
1. 緩衝區提前分配,可以使用堆的空間,也可以使用棧的空間。所以分配方式有如下兩種:
123class
MyClass {…};
char
*buf=
new
char
[N*
sizeof
(MyClass)+
sizeof
(
int
)];
char
buf[N*
sizeof
(MyClass)+
sizeof
(
int
)];
2. 對象的構造
1MyClass * pClass=
new
(buf) MyClass;
//使用placement new
3. 對象的銷燬
一旦這個對象使用完畢,你必須顯式的調用類的析構函數進行銷燬對象。但此時內存空間不會被釋放,以便其他的對象的構造。
1pClass->~MyClass();
4. 內存的釋放
如果緩衝區在堆中,那麼調用delete[] buf 進行內存的釋放。如果在棧中,那麼在其作用域內有效,跳出作用域,內存自動釋放。
注意:
在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
以上爲網友整理的資料。下面爲一個C++權威參考資料:
operator new分爲三種形式(前2種不調用構造函數,這點區別於new operator):
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不可重載。
例子:
12345678910111213141516171819202122232425262728// 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;
}
輸出:myclass constructed
重載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; |
輸出: Logging an allocation of 12 bytes for new object 'Base instance 1'
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 ); |
其中,nothrow爲 C++預定義的std::nothrow_t類型全局常量。
我的理解
operator new可以被重載,它的三個版本本質上是函數,它們只分配空間,不調用構造函數。而new、delete(也叫做new operator、delete operator)不可被重載,它們是運算符,分配空間,並且調用構造函數。
相關博客:
http://blog.csdn.net/xiaobo68688/article/details/5650516