c++中的new/delete

c++中的new/delete

new/delete究竟做了些什麼?
  在理解這個問題之前,我們先看一下下面的這段程序,有這麼一個程序段:
    class A
    {
    public:
      A() {   cout<<"A is here!"<<endl;   }
      ~A(){   cout<<"A is dead!"<<endl;   }
    private:
      int i;
    };
    A* pA=new A;
    delete pA;
  在這個簡單的程序段裏面,new/delete究竟做了些什麼?
  實際上,這段程序裏面隱含調用了一些我們沒有看到的東西,那就是:
  static void* operator new(size_t sz);
  static void operator delete(void* p);
  值得注意的是,這兩個函數都是static的,所以如果我們重載了這2個函數(我們要麼不重載,要重載就要2個一起行動),也應該聲明爲static的,如果我們沒有聲明,系統也會爲我們自動加上。另外,這是兩個內存分配原語,要麼成功,要麼沒有分配任何內存。
  size_t是什麼東西呢?我在第一次看到這個動動的時候也是十分的困惑,畢竟以前沒有見過。size_t在<cstddef>中定義,是一種無符號整數類型(不一定是int),用來保存對象的大小,這一用法是從C語言中借用過來的,現在你應該明白了吧(我學習的時候可是鬱悶了好幾天,沒有人可以問,因爲不知道有個csdn:)
  new A;實際上做了2件事:調用opeator new,在自由存儲區分配一個sizeof(A)大小的內存空間;然後調用構造函數A(),在這塊內存空間上類磚砌瓦,建造起我們的對象。同樣對於delete,則做了相反的兩件事:調用析構函數~A(),銷燬對象,調用operator delete,釋放內存。不過需要注意的是,new分配一塊內存的時候,並沒有對這塊內存空間做清零等任何動作,只是拿了過來,這塊內存上放的仍然是原來的數據(垃圾數據),delete的時候,也只是釋放這塊內存,歸還給操作系統,上面的數據還在上面,所以delete pA之後,pA的值沒變,他指向的那塊內存的值也沒有變,不過似乎有什麼問題,我們看一下下面的這個程序段:
int *p=new int(50000);
cout<<*p<<"   "<<p<<endl;
delete p;
cout<<*p<<"   "<<p<<endl;
  我們可以清楚地看到,指針p存放的數據仍然是原來的地址,但是*p的內容卻發生了變化,在我的機器上(win2000, VC6)始終是-572662307,不清楚這是爲什麼,難道系統做了什麼手腳?還望高手指教。  
  在這裏我們可以看到,new的工作實際上就是保證相互分離的存儲分配和初始化工作能夠很好的在一起工作,不過這裏可能讓初學者迷惑的是,我們定義了一個帶有參數的new,但是我們用的時候卻沒有顯式的去調用,而是讓系統“神祕”的去提供這個參數。是的,這樣做毫無疑問增加了複雜性,但是讓基類獲取了爲一集派生類提供分配和釋放服務的能力。  

  new/delete有什麼好處和壞處?
  從C程序員轉換過來的C++程序員總是有個困惑:new/delete到底究竟和C語言裏面的malloc/free比起來有什麼優勢?或者是一樣的?
  其實,就算我不說,你也應該很清楚了,new/delete當然比malloc/free要好,要不然,爲什麼還引進這個東東呢?其實通過上面的分析,我們看到了new/delete實際上做了很多malloc/free沒有做的事情:malloc/free只是對內存進行分配和釋放;new/delete還負責完成了創建和銷燬對象的任務。
  另外,new的安全性要高一些,因爲他返回的就是一個所創建的對象的指針,對於malloc來說返回的則是void*,還要進行強制類型轉換,顯然這是一個危險的漏洞。
  最後,我們可以對new/delete重載,使內存分配按照我們的意願進行,這樣更具有靈活性,malloc則不行。
  不過,new/delete也並不是十分完美,大概最大的缺點就是效率低(針對的是缺省的分配器),原因不只是因爲在自由存儲區上分配(和棧上對比),具體的原因目前不是很清楚,不過在MCD上說了2個可能的原因:
  1、new只是對於堆分配器(malloc/realloc/free)的一個淺層包裝,沒有針對小型的內存分配做優化。
  2、缺省分配器具有通用性,它管理的是一塊內存池,這樣的管理往往需要消耗一些額外空間。

  各種各樣的new
  一般來說,new有很多種形式,不過真的歸納起來,也就是2種:
  1、最常用的形式:
    void *operator new(std::size_t sz)   throw(std::bad_alloc);   (普通的)
    void *operator new[](std::size_t sz) throw(std::bad_alloc);   (數組的)
    void *operator new(std::size_t sz);
    void *operator new[](std::size_t sz)
  這一種大家用得最爲頻繁,我就不舉例子了。
  2、放置new形式:
    void *operator new(std::size_t count, void *ptr)   throw();   (普通的)
    void *operator new[](std::size_t count, void *ptr) throw();   (數組的)
  要使用這種方式,必須包含頭文件<new>。這個機制引入的初始目的是爲了解決2個相關的問題:
    1、把一個對象放在某個特定位置;
    2、在某個特定分配區裏面分配對象;
    但是引入之後,發現這種機制遠超出了簡單的存儲分配機制,我們可以給特定的存儲位置關聯任意的邏輯性值,這樣一來,new就有了一種通用資源管理器的作用。同時第二個參數,也被擴展成了任意的可以識別的類型,並且配備了相應的nothrow版本:
    void* operator new(std::size_t, const std::nothrow_t&)   throw();
    void* operator new[](std::size_t, const std::nothrow_t&) throw();

  new能夠返回NULL麼?
  我們經常看到有很多初學者喜歡寫如下代碼:
  A* p=new A();
  if(p==NUL) ....
  寫下這段代碼的可能是受到了一些書上錯誤的影響,因爲new A()從來就不可能返回NULL,如果在這個過程中用完了內存,那麼他就會拋出bad_alloc異常,絕對不會返回NULL,如果你想讓他返回null,應該用new(nothrow) A(),而不是new A()。不過從異常的觀點來看,這實際上是一種倒退,我們應該儘量迴避。

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