C++ 深入解析new關鍵字,::new、operator new函數,placement new表達式

1. new運算符和operator new()

new: 指我們在C++裏通常用到的關鍵字。比如 A *a=new A,爲了實現這一目的,應用程序需要重載new運算符和delete運算符以控制內存分配的過程。對於new來說,有new和::new之分,前者位於std命名空間中

operator new(或operator new[]):是一個標準庫函數,並不是運算符。對於operator new 來說,分爲全局重載類重載,全局重載是void* ::operator new(size_t size),在類中重載形式void* A::operator new(size_t size)。還要注意的是這裏的operator new()完成的操作一般只是分配內存,事實上系統默認的全局重載也只是調用malloc分配內存,並且返回一個void* 指針。

2.new 和 operator new之間的聯繫

(1)A* a=new A ; 這行代碼實際執行三步操作

           1)new表達式調用一個名爲operator new(或者operator new[])的標準庫函數。該函數分配一塊足夠大的、原始的、未命名的內存空間以便存儲A類型的對象(或者對象數組);

           2)編譯器運行相應地構造函數以構造這些對象,併爲其傳入初始值;

           3)對象被分配了空間並構造完成,返回一個指向該對象的指針

      事實上,如果類A重載了operator new,那麼將調用A::operator new(size_t size),如果沒有重載,就調用全局函數::operator new(size_t size),全局new操作符由c++默認提供。

(2)operator new接口和operator delete接口

        標準庫定義了以下8個版本。其中前4個版本可能拋出bad_alloc異常,後4個版本不會拋出異常;

                  //這些版本可能拋出異常

                  void* operator new (size_t);

                  void*  operator new[] (size_t);

                  void* operator delete (void*) noexcept;

                  void* operator delete[] (void*) noexcept;

 

                  void* operator new (size_t, nothrow_t&)  noexcept;

                  void*  operator new[] (size_t, nothrow_t&) noexcept;

                  void* operator delete (void*, nothrow_t&) noexcept;

                  void* operator delete[] (void*,nothrow_t&) noexcept;

        對於operator new函數:要實現不同的內存分配行爲,應該重載operator new,而不是new。

        operator new就像operator + 一樣,是可以重載的。如果類中沒有重載operator new,那麼調用的就是全局的::operator new 來完成堆的內存申請。同理,operator new[]、operator delete、operator delete[]也是可以重載的。

        儘管operator new函數和operator delete函數一般用於new表達式,然而它們畢竟是標準庫的兩個普通函數,因此普通的代碼也可以直接調用它們

3. ::new與new

       默認情況下編譯器會將new這個關鍵字翻譯成全局::operator new和相應的構造函數。

       但在有的情況下,用戶自己會在類中重載operator new。這種情況下,編譯器默認會使用類中重載的operator new(本質上因爲編譯器會從命名空間由內向外查找自己想要的函數,選用第一個)。

        如果我們想要繼續使用默認的operator new,就應該寫成::new ,字面意思是調用最外層命名空間中的operator new。

        值得一提的是,全局operator new也是可以被重載的。通過這種方式,我們可以改變所有new的部分行爲。

4. placemen new 定位new表達式

 功能

        定位new允許我們在一個特定的、預先分配的內存地址上構造對象,實參不侷限於動態內存

 形式

                  new  ( place_address )  type

                  new  ( place_address )  type (initializers)

                  new  ( place_address )  type [size]

                  new  ( place_address )  type [size]  { braced initializer list }

         其中place_address必須是一個指針,同時在initializers中提供了一個(可能爲空的)以逗號分隔的初始值列表,該初始值列表將用於構造新分配的對象。

 

1)當僅通過一個地址值調用時,定位new使用operator new (size_t, void*)“分配”它的內存。這是一個我們無法重載的operator new版本。該函數不分配任何內存,它只是簡單地返回指針實參;然後由new表達式負責在指定的地址初始化對象以完成整個工作。

        eg.     char*  ptr = new  char[ sizeof(T) ];             //allocate memory

                  T*  tptr = new(ptr) T;                                  //construct in allocated storage ("place")

                  tptr->~T();                                                 //destruct

                  delete[]  ptr;                                              //deallocate memory

2) 如果不是1)情況,則表達式返回place_address地址。

      (C++ Primer 第五版 P753)

        eg.   Token &Token::operator=( const std::string &s)

                 {

                      if ( tok ==STR)                         //如果當前存儲的是string,可以直接賦值

                            sval = s;

                      else

                            new(&sval) string(s);         //否則需要先構造一個string

                      tok = STR;                              //更新判別式

                      return *this;

                  }

 

        可以看出,通過定位new表達式,我們可以實現保存一塊內存,反覆構造析構,這樣可以省略中間的多次分配內存。由於malloc內存會導致系統調用,這可以節省大量的系統開銷

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