Solmyr和Zero的故事 —— 記憶體,最後一塊 (轉)

worldblog發表於2007-12-12
Solmyr和Zero的故事 —— 記憶體,最後一塊 (轉)[@more@]  “Shit!又當機了。我已經在這平臺上工作了一個月了。可當機的次數比我在這個月裡被女孩甩的次數還多。為什麼?還不是這該死的平臺,為什麼掌上的就和願意和我說話的女孩一樣少?”Solmyr抱怨道。

 :namespace prefix = o ns = "urn:schemas--com::office" />

“兄弟,怎麼了。”Zero問。

 

  “Zero是我們這組的主員,他懂得很多,人長的也帥,很討女孩子的歡心。甚至連美工組的Lili(長得比孫燕姿還好看)也暗戀他。”。Solmyr一邊想,一邊說,“老大,你看,又當機了。為什麼我每次用動態記憶體超過10次,就crash了呢?”

 

“我知道你原來是程式設計師,也許到現在,你還念念不忘那GC (垃圾收集機制,我說還不如叫上帝也哭泣-God  Cry),可你要知道,你現在是在用C++,重要的是。”

 

  “效率,我……”忽然,Solmyr覺得嘴角邊似乎有液體流過。那是Solmyr的口水。每次提到效率,Solmyr總要流口水,就像看到漂亮MM,Solmyr要噴鼻血一樣。

 

  “C++中,關於動態記憶體的是new and delete。”

 

“我知道,”Solmyr急於表現自己,想證明自己對C++並不是一無所知,“我正在讀tt Meyer的More Effective C++。在C++中,new operator是C++內建的行為。任何人(也許除了Bjarne Stroustrup)都無法改變。new operator先一個名為operator new的動態申請記憶體。標準就像這樣:

 

  void* operator new(size_t size);

  然後在傳回的void*指標上進行構造的行為。而delete operator則先析構,然後呼叫名為operator delete的函式,標準形式就像這樣:

  void operator delete(void* pToDeAlloc)

// GotW中說即使在指標引數後加上size_t size,仍然是標準形式。

// size_t size的作用是檢查所要卸除的記憶體是否是期望的大小,如果在類層次中定義的話,只要基類是virtual destructor,那麼size可以// 確保是正確大小。

placement new……。”

 

  “唔,說得不錯,有進步。關於placement new/delete,先知Meyer[1]有詳細的論述。原來的placement new僅僅是這樣:

  void* operator new(size_t, void* pMem) { return pMem; }

‘ 隨著時間過去,任何‘要求額外引數’的 operator new 版本,也都漸漸採用 placement new 這個術語。事實上這個術語已經被銘記於 C++ 標準規格中。因此,當 C++ 程式設計師談到所謂的 placement new 函式,他們所談的可能是上述那個‘需要額外一個 void* 引數,用以指出物件置於何處’的版本,但也可能是指那些‘所需引數比單一而必要之 size_t 引數更多’的任何 operator new 版本,包括上述函式,也包括其他‘引數更多’的 operator new 函式。’——引自[1]

 

現在我們來考慮一個問題,如果在operator new結束申請記憶體後,建構函式丟擲了異常,那麼已經申請的記憶體誰來回收,答案當然是。因為整個new operator還未結束。所以你無法獲得控制權。如果你對operator new/delete進行了過載,那麼編譯器會呼叫那個operator  delete呢?由於不同的operator new可能透過不同的方法獲得記憶體,而讓不知道怎樣分配的operator delete去釋放記憶體顯然是不負責任的。所以編譯器假定哪個operator deleteoperator new有相同的引數(除了size_t sizevoid* pDeAlloc),那麼那個operator delete便知道operator new怎樣獲得記憶體。建構函式丟擲異常後,也會呼叫與operator new相同引數的operator delete來釋放記憶體。”

 

  “那麼,我該怎麼解決現在的問題呢?”

“別那麼急,已經下午五點了。該回家了,別讓人以為程式設計師是夜遊神。回家看看Effective C++第二部分和GotW9, GotW10。明天再說吧。”

 

第二天一早,Solmyr啃著大餅走進了辦公室,看到Guru Zero早已姿勢優雅地坐在前收發E,不禁自慚形穢,連忙放下手中的大餅,跑去和Zero說:“老大,昨天晚上,我挑燈夜讀,總結出兩點:

1.  如果你寫了一個operator new,請對應寫一個operator delete

2.  不要delete不是自己new來的記憶體。

“嗯,不錯,怎麼我看上去,就象是Effective C++中的條款呢?你有沒有自己想過關於placement delete的問題?”

placement delete有什麼問題嗎?”Solmyr一臉茫然的問道。

“你有沒有試過把一塊用placement new申請得到的記憶體用placement delete卸除掉呢?不妨你現在試試看。”

 

只見Solmyr跑到一臺電腦前,兩手如飛在鍵盤上敲擊,可是我們能聽到的只有他的唉聲嘆氣和編譯器的哇哇大叫。Solmyr實在是沒辦法,只能向Zero求教。Zero喝了一口咖啡,說道:“我們平時寫的那些要求額外引數的operator delete只有在建構函式丟擲異常時,才會被編譯器自動呼叫,而我們是不可能手工呼叫到任何帶有額外引數的delete的,為什麼沒有一個內建的‘placement delete’來與‘placement new’相匹配的原因在於沒有辦法保證它被正確使用,在C++型別系統中,無法推斷一個指標從哪裡獲得它指向的記憶體,可能是指向heap,也可能是stack。所以……”

 

“所以當我們確實知道一個指標它是怎麼獲得它所指向的記憶體時,我們可以這樣:

template void destroy(T* p, Arena& a)

  {

  if (p) {

  p->~T();  // explicit destructor call

  a.deallocate(p);

  }

  }

這樣,就不會有資源洩漏了。”[2]

“可是我怎麼解決我那稀有的記憶體的問題呢?”

“標準庫中的allocatorboost中的pool可以解決記憶體管理的問題,不必每次呼叫operator new,從而少了一些開銷,不過早上我剛收到客戶的mail,他們說準備在他們的裝置上再加12MB的記憶體。”

“……”Solmyr鬱悶中。

 

  [1] Counting in C++ CUJ 1998.4 中文譯文 陳崴 (程式設計師雜誌:2001.8 雜誌上少了placement new and placement delete)

   [2]'s C++ Style and Technique FAQ  


來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/10752043/viewspace-991596/,如需轉載,請註明出處,否則將追究法律責任。

相關文章