<ruby id="bdb3f"></ruby>

    <p id="bdb3f"><cite id="bdb3f"></cite></p>

      <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
        <p id="bdb3f"><cite id="bdb3f"></cite></p>

          <pre id="bdb3f"></pre>
          <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

          <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
          <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

          <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                <ruby id="bdb3f"></ruby>

                ThinkChat2.0新版上線,更智能更精彩,支持會話、畫圖、視頻、閱讀、搜索等,送10W Token,即刻開啟你的AI之旅 廣告
                # Item 52: 如果編寫了 placement new,就要編寫 placement delete 作者:[Scott Meyers](http://aristeia.com/) 譯者:[fatalerror99 (iTePub's Nirvana)](mailto:fatalerror9999@hotmail.com?subject=Item%2049) 發布:[http://blog.csdn.net/fatalerror99/](http://blog.csdn.net/fatalerror99/) 在 C++ 動物園中,placement new 和 placement delete 并不是最常遇到的野獸,所以如果你和它們不熟也不必擔心。作為替代,回想一下 [Items 16](http://blog.csdn.net/fatalerror99/archive/2005/07/20/430119.aspx) 和 [17](http://blog.csdn.net/fatalerror99/archive/2005/07/21/431251.aspx),當你寫下一個這樣的 new 表達式, ``` Widget *pw = new Widget; ``` 有兩個函數會被調用:一個是 operator new 用于分配內存,第二個是 Widget 的 default constructor(缺省構造函數)。 假設第一個調用成功,而第二個調用導致拋出一個 exception(異常)。這種情況下,第 1 步中完成的內存分配必須被撤銷。否則就是一個內存泄漏。客戶代碼不可能回收這些內存,因為,如果 Widget 的 constructor(構造函數)拋出一個 exception(異常),pw 根本就沒有被賦值。對于客戶來說無法得到指向應該被回收的內存的指針。所以撤銷第 1 步的職責必然落在了 C++ runtime system(C++ 運行時系統)的身上。 runtime system(運行時系統)恰當地調用與它在第 1 步中調用的 operator new 的版本相對應的 operator delete,但是只有在它知道哪一個 operator delete——可能有許多——最恰當的時候它才能做到這一點。如果你正在擺弄具有常規的 signatures(識別特征)的 new 和 delete 版本,這不成問題,因為常規的 operator new, ``` void* operator new(std::size_t) throw(std::bad_alloc); ``` 對應常規的 operator delete: ``` void operator delete(void *rawMemory) throw();? // normal signature ??????????????????????????????????????????????? // at global scope void operator delete(void *rawMemory,?????????? // typical normal ???????????????????? std::size_t size) throw(); // signature at class ??????????????????????????????????????????????? // scope ``` 當你只使用 new 和 delete 的常規形式時,runtime system(運行時系統)找出知道如何撤銷 new 所做的事情的 delete 沒什么麻煩。然而,當你開始聲明 operator new 的非常規形式——帶有額外參數的形式的時候,which-delete-goes-with-this-new(哪一個 delete 和這個 new 配對)的問題就出現了。 例如,假設你編寫了一個 class-specific(類專用)的 operator new,它需要一個用于記錄分配信息的 ostream 的規格描述,而你又編寫了一個常規的 class-specific(類專用)的 operator delete: ``` class Widget { public: ? ... ? static void* operator new(std::size_t size,????????????? // non-normal ??????????????????????????? std::ostream& logStream)?????? // form of new ??? throw(std::bad_alloc); ? static void operator delete(void *pMemory??????????????? // normal class- ????????????????????????????? std::size_t size) throw();?? // specific form ?????????????????????????????????????????????????????????? // of delete ? ... }; ``` 這個設計是成問題的,但是在我們探究為什么之前,我們需要做一個簡要的術語說明。 當一個 operator new function 持有額外的參數(除了那個必要的 size_t 參數),這個 function 就被稱為 new 的 _placement_ 版本。前面那個 operator new 就是這樣一個 placement 版本。有一個特別有用的 placement new,它持有一個指針,這個指針指定了一個 object 被構造的位置。那個 operator new 如下: ``` void* operator new(std::size_t, **void *pMemory**) throw();?? // "placement ????????????????????????????????????????????????????????? // new" ``` new 的這個版本是 C++ 標準庫的一部分,只要 #include &lt;new&gt; 你就可以訪問它。需要指出,這個 new 用于 vector 內部,在 vector 的尚未使用的空間內創建 objects。它也是最初的 placement new。實際上,這就是這類函數被稱為 _placement new_ 的來歷。這就意味著術語 "placement new" 被賦予了更多的含義。大多數情況下,當人們談到 placement new,他們談的就是這個特定的函數,持有一個 void\* 類型的額外參數的 operator new。較少情況下,他們談的是持有額外參數的 operator new 的任意版本。根據上下文通常可以搞清楚任何曖昧,重要的是要了解到通用術語 "placement new" 意味著持有額外參數的 new 的任意版本,因為短語 "placement delete"(過一會兒我們就會遇到它)直接起源于它。 我們讓我們先返回到 Widget class 的 declaration(聲明),就是我說設計成問題的那個。麻煩就在于這個 class 會引發微妙的 memory leaks(內存泄漏)。考慮如下客戶代碼,在動態創建一個 Widget 時,它將在 cerr 記錄分配信息: ``` Widget *pw = new (std::cerr) Widget; // call operator new, passing cerr as ???????????????????????????????????? // the ostream; _this leaks memory_ ???????????????????????????????????? // _if the Widget constructor throws_ ``` 重申一次,如果內存分配成功而 Widget constructor(構造函數)拋出一個 exception(異常),runtime system(運行時系統)有責任撤銷 operator new 所執行的分配。然而,runtime system(運行時系統)不能真正了解被調用的 operator new 版本是如何工作的,所以它自己無法撤銷那個分配。runtime system(運行時系統)轉而尋找一個和 operator new 持有相同數量和類型額外參數的 operator delete 版本,而且,如果它找到了,它將調用它。在當前情況下,operator new 持有一個 ostream& 類型的額外參數,所以相應的 operator delete 應該具有這樣的 signature(識別特征): ``` void operator delete(void *, **std::ostream&**) throw(); ``` 與 new 的 placement 版本類似,持有額外參數的 operator delete 版本被稱為 _placement deletes_。當前情況下,Widget 沒有聲明 operator delete 的 placement 版本,所以 runtime system(運行時系統)不知道如何撤銷所調用的 placement new 所做的事情。結果,它什么都不做。在本例中,如果 Widget constructor(構造函數)拋出一個 exception(異常),沒有 _operator delete_ 可以被調用! 規則很簡單:如果一個帶有額外參數的 operator new 沒有帶有同樣額外參數的 operator delete 相匹配,當一個由 new 生成的內存分配需要撤銷的時候沒有 operator delete 可以被調用。為了消除前面的代碼中的 memory leak(內存泄漏),Widget 需要聲明一個與 logging placement new 相對應的 placement delete: ``` class Widget { public: ? ... ? static void* operator new(std::size_t size, std::ostream& logStream) ??? throw(std::bad_alloc); ? static void operator delete(void *pMemory) throw(); ? **static void operator delete(void *pMemory, std::ostream& logStream)** ??? **throw();** ? ... }; ``` 這樣改變之后,如果從下面這個語句的 Widget constructor(構造函數)中拋出一個 exception(異常), ``` Widget *pw = new (std::cerr) Widget;?? // as before, but no leak this time ``` 相應的 placement delete 自動被調用,而這就讓 Widget 確保沒有內存被泄漏。 然而,考慮以下情況會發生什么,如果沒有拋出 exception(異常)(這是通常的情況)而我們的客戶代碼中又有一個 delete: ``` delete pw;??????????????????????????? // invokes the normal ????????????????????????????????????? // operator delete ``` 就像注釋中所說的,這樣將調用常規 operator delete,而不是 placement 版本。只有在調用一個與 placement new 相關聯的 constructor(構造函數)時發生一個 exception(異常),placement delete 才會被調用。將 delete 施加于一個指針(諸如上面的 pw)絕對不會引起一個 delete 的 placement 版本的調用。絕對不會。 這就意味著為了預防所有與 new 的 placement 版本相關的 memory leaks(內存泄漏),你必須既提供常規 operator delete(用于構造過程中沒有拋出 exception(異常)時),又要提供一個持有與 operator new 相同的 extra arguments(額外參數)的 placement 版本(用于相反情況)。這樣,你就再也不會因為微妙的 memory leaks(內存泄漏)而睡不著覺了。好吧,至少是不會因為這里這些微妙的 memory leaks(內存泄漏)。 順便說一下,因為 member function(成員函數)的名字會覆蓋外圍的具有相同名字的函數(參見 [Item 33](http://blog.csdn.net/fatalerror99/archive/2005/09/13/479864.aspx)),你需要小心避免用 class-specific(類專用)的 news 覆蓋你的客戶所希望看到的其它 news(包括其常規版本)。例如,如果你有一個只聲明了一個 operator new 的 placement 版本的 base class(基類),客戶將發現 new 的常規形式對他們來說無法使用: ``` class Base { public: ? ... ? static void* operator new(std::size_t size,?????????? // this new hides ??????????????????????????? std::ostream& logStream)??? // the normal ??? throw(std::bad_alloc);????????????????????????????? // global forms ? ... }; Base *pb = new Base;??????????????????????? // error! the normal form of ??????????????????????????????????????????? // operator new is hidden Base *pb = new (std::cerr) Base;??????????? // fine, calls Base's ??????????????????????????????????????????? // placement new ``` 同樣,derived classes(派生類)中的 operator news 覆蓋 operator news 的全局和繼承來的版本的 operator new: ``` class Derived: public Base {?????????????????? // inherits from Base above public: ? ... ? static void* operator new(std::size_t size)? // redeclares the normal ????? throw(std::bad_alloc);?????????????????? // form of new ? ... }; Derived *pd = new (std::clog) Derived;???????? // error! Base's placement ?????????????????????????????????????????????? // new is hidden Derived *pd = new Derived;???????????????????? // fine, calls Derived's ?????????????????????????????????????????????? // operator new ``` [Item 33](http://blog.csdn.net/fatalerror99/archive/2005/09/13/479864.aspx) 討論了這種名字覆蓋的需要考慮的細節,如果打算編寫內存分配函數,你要記住,在缺省情況下,C++ 在全局范圍提供如下形式的 operator new: ``` void* operator new(std::size_t) throw(std::bad_alloc);????? // normal new void* operator new(std::size_t, void*) throw();???????????? // placement new void* operator new(std::size_t,???????????????????????????? // nothrow new — ?????????????????? const std::nothrow_t&) throw();????????? // see [Item 49](http://blog.csdn.net/fatalerror99/archive/2006/02/28/612673.aspx) ``` 如果你在一個 class 中聲明了任何 operator news,都將覆蓋所有這些標準形式。除非你有意防止 class 的客戶使用這些形式,否則,除了你創建的任何自定義 new 形式以外,還要確保它們都可以使用。當然,還要確保為每一個你使其可用的 operator new 提供相應的 operator delete。如果你要這些函數具有通常的行為,只需要讓你的 class-specific(類專用)版本去調用 global(全局)版本即可。 達到這種效果的一個簡單方法是創建一個包含 new 和 delete 的全部常規形式的 base class(基類): ``` class StandardNewDeleteForms { public: ? **// normal new/delete** ? static void* operator new(std::size_t size) throw(std::bad_alloc) ? { return ::operator new(size); } ? static void operator delete(void *pMemory) throw() ? { ::operator delete(pMemory); } ? **// placement new/delete** ? static void* operator new(std::size_t size, void *ptr) throw() ? { return ::operator new(size, ptr); } ? static void operator delete(void *pMemory, void *ptr) throw() ? { return ::operator delete(pMemory, ptr); } ? **// nothrow new/delete** ? static void* operator new(std::size_t size, const std::nothrow_t& nt) throw() ? { return ::operator new(size, nt); } ? static void operator delete(void *pMemory, const std::nothrow_t&) throw() ? { ::operator delete(pMemory); } }; ``` 想要在標準形式之外增加自定義形式的客戶就能夠使用 inheritance(繼承)和 using declarations(使用聲明)(參見 [Item 33](http://blog.csdn.net/fatalerror99/archive/2005/09/13/479864.aspx))來得到標準形式: ``` class Widget: public StandardNewDeleteForms {?????????? // inherit std forms public: ?? using StandardNewDeleteForms::operator new;????????? // make those ?? using StandardNewDeleteForms::operator delete;?????? // forms visible ?? static void* operator new(std::size_t size,????????? // add a custom ???????????????????????????? std::ostream& logStream)?? // placement new ???? throw(std::bad_alloc); ?? static void operator delete(void *pMemory,?????????? // add the corres- ?????????????????????????????? std::ostream& logStream) // ponding place- ??? throw();??????????????????????????????????????????? // ment delete ? ... }; ``` **Things to Remember** * 在編寫一個 operator new 的 placement 版本時,確保同時編寫 operator delete 的相應的 placement 版本。否則,你的程序可能會發生微妙的,斷續的 memory leaks(內存泄漏)。 * 當你聲明 new 和 delete 的 placement 版本時,確保不會無意中覆蓋這些函數的常規版本。
                  <ruby id="bdb3f"></ruby>

                  <p id="bdb3f"><cite id="bdb3f"></cite></p>

                    <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
                      <p id="bdb3f"><cite id="bdb3f"></cite></p>

                        <pre id="bdb3f"></pre>
                        <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

                        <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
                        <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

                        <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                              <ruby id="bdb3f"></ruby>

                              哎呀哎呀视频在线观看