1. 程式人生 > >C ++的new、delete

C ++的new、delete

當寫出p = new P();這樣的程式碼的時候, 實際上有兩步操作, 首先分配記憶體,然後在分配好的記憶體之上初始化類成員.

二步是有建構函式完成的, 第一步就是new函式的工作.

全域性的new有六種過載形式,
void *operator new(std::size_t count)
 throw(std::bad_alloc);  //一般的版本

void *operator new(std::size_t count,  //相容早版本的new
 const std::nothrow_t&) throw();  //記憶體分配失敗不會丟擲異常

void *operator new(std::size_t count, void *ptr) throw();

 //placement版本
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();

所以, 剛才的用法, 就是使用new函式的一種過載形式.
如果A這個物件以同樣實行過載了new函式的化, 作為成員函式
會被優先呼叫.

C++的各種new簡介

1.new T

第一種new最簡單,呼叫類的(如果過載了的話)或者全域性的operator new分配空間,然後用

型別後面列的引數來呼叫建構函式,用法是
new TypeName(initial_args_list). 如果沒有引數,括號一般可以省略.例如

int *p=new int;
int *p=new int(10);
int *p=new foo("hello");

通過呼叫delete來銷燬:
delete p;

2. new T[]
這種new用來建立一個動態的物件陣列,他會呼叫物件的operator new[]來分配記憶體(如果
沒有則呼叫operator new,搜尋順序同上),然後呼叫物件的預設建構函式初始化每個物件
用法:
new TypeName[num_of_objects];
例如
int *p= new int[10];

銷燬時使用operator delete[]

3.new()T 和new() T[]
這是個帶引數的new,這種形式的new會呼叫operator new(size_t,OtherType)來分配記憶體
這裡的OtherType要和new括號裡的引數的型別相容,

這種語法通常用來在某個特定的地址構件物件,稱為placement new,前提是operator new
(size_t,void*)已經定義,通常編譯器已經提供了一個實現,包含<new>標頭檔案即可,這個
實現只是簡單的把引數的指定的地址返回,因而new()運算子就會在括號裡的地址上建立
物件

需要說明的是,第二個引數不是一定要是void*,可以識別的合法型別,這時候由C++的過載
機制來決定呼叫那個operator new

當然,我們可以提供自己的operator new(size_,Type),來決定new的行為,比如
char data[1000][sizeof(foo)];
inline void* operator new(size_t ,int n)
{
 return data[n];
}

就可以使用這樣有趣的語法來建立物件:
foo *p=new(6) foo(); //把物件建立在data的第六個單元上
的確很有意思
標準庫還提供了一個nothrow的實現:
void* operator new(std::size_t, const std::nothrow_t&) throw();
void* operator new[](std::size_t, const std::nothrow_t&) throw();

就可以實現呼叫new失敗時不丟擲異常
new(nothrow) int(10);
// nothrow 是std::nothrow_t的一個例項


placement new 建立的物件不能直接delete來銷燬,而是要呼叫物件的析夠函式來銷燬對
象,至於物件所佔的記憶體如何處理,要看這塊記憶體的具體來源

4. operator new(size_t)
這個的運算子分配引數指定大小的記憶體並返回首地址,可以為自定義的類過載這個運算子,
方法就是在類裡面宣告加上
void *operator new(size_t size)
{
 //在這裡分配記憶體並返回其地址
}
無論是否宣告,類裡面過載的各種operator new和operator delete都是具有static屬性的

一般不需要直接呼叫operator new,除非直接分配原始記憶體(這一點類似於C的malloc)
在衝突的情況下要呼叫全域性的operator加上::作用域運算子:
::operator new(1000); // 分配1000個位元組

返回的記憶體需要回收的話,呼叫對應的operator delete

5.operator new[](size_t)

這個也是分配記憶體,,只不過是專門針對陣列,也就是new T[]這種形式,當然,需要時可以
顯式呼叫

6.operator new(size_t size, OtherType other_value)
和operator new[](size_t size, OtherType other_value)
參見上面的new()


需要強調的是,new用來建立物件並分配記憶體,它的行為是不可改變的,可以改變的是各種

operator new,我們就可以通過過載operator new來實現我們的記憶體分配方案.

1) new operator,也叫new表示式 ,就是我們慣常使用的new

      string* ps = new string("abc");
      上面這個new表示式完成了兩件事情:申請記憶體和初始化物件

         2) operator new,也叫new操作符。

            plan new   ,  nothrow new  , placement new (不知將placement new 歸為此類是否正確

            1>> plan new
                
          new操作符類似於C語言中的malloc,只是負責申請記憶體,例如:
          void* buffer = operator new(sizeof(string));
          注意這裡多了一個operator。

          在C++中是這樣定義的:

          void* operator new(std::size_t) throw(std::bad_alloc);
          void operator delete(void *) throw();

          提示:plain new在分配失敗的情況下,丟擲異常std::bad_alloc而不是返回NULL,
                      因此通過判斷返回值是否為NULL是徒勞的。 

     2>> nothrow new  是不丟擲異常的運算子new的形式。

           nothrow new在失敗時,返回NULL。定義如下:
          void * operator new(std::size_t,const std::nothrow_t&) throw();
          void operator delete(void*) throw(); 
        例:

           1. unsigned char *p = new(nothrow) unsigned char[length];  
           2. if(p == NULL) cout<<"allocate failed!"<<endl;  
           3. // ...  
           4. delete []p; 

            3>> placement new 它用於在給定的記憶體中初始化物件
                這種new允許在一塊已經分配成功的記憶體上重新構造物件或物件陣列。
                不用擔心記憶體分配失敗,因為它根本不分配記憶體,它做的唯一一件事情就是呼叫物件的建構函式。

                定義如下:
                    void* operator new(size_t,void*);
                    void operator delete(void*,void*);

                placement new是在指定位置初始化物件,也就是呼叫了建構函式,因此與之對應的就是析構函數了,
      必須顯示的呼叫類的解構函式釋放物件。但此時記憶體空間仍然不會被釋放,以供其他的物件使用。
      組後仍然要呼叫 delete 釋放記憶體空間

                class A
                {
                  public:

                    A();
                    ~A();

                  private:
                    int a ;

                    int b;
                };
                void *pv = operator new(sizeof(A)) ;
                pv = new(pv) (sizeof(A)) ;
                      static_cast(A*)(pv)->~A();
                pv = NULL ;


                主要應用場合:
                    1>> 的主要用途就是反覆使用一塊較大的動態分配的記憶體來構造不同型別的物件或者他們的陣列。
                    2>> 構造起來的物件或其陣列,要顯示的呼叫他們的解構函式來銷燬,千萬不要使用delete。
       

與new對應的delete語法只有兩種:

    1. delete operator   delete表示式

      delete表示式和new表示式對應,完成物件的析構和記憶體的釋放操作。
      string* ps = new string("abc");
      delete ps; //呼叫delete表示式,先析構再釋放

    2. operator delete   delete操作符。

      delete 操作符只是用於記憶體的釋放,和C語言中的free相似。
      例如:
        void* buffer = operator new(sizeof(string));
        operator delete(buffer); //釋放