Placement new和operator new解釋

原文地址: 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 *operatornew(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. 緩衝區提前分配,可以使用堆的空間,也可以使用棧的空間。所以分配方式有如下兩種:

1
2
3
class MyClass {…};
char *buf=new char[N*sizeof(MyClass)+sizeof(int)];
char buf[N*sizeof(MyClass)+sizeof(int)];

2. 對象的構造

1
MyClass * pClass=new(buf) MyClass; //使用placement new

3. 對象的銷燬 
      一旦這個對象使用完畢,你必須顯式的調用類的析構函數進行銷燬對象。但此時內存空間不會被釋放,以便其他的對象的構造。

1
pClass->~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不可重載。

例子:

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;
}

輸出: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 *operatornew(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


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章