<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>

                企業??AI智能體構建引擎,智能編排和調試,一鍵部署,支持知識庫和私有化部署方案 廣告
                # Item 50: 領會何時替換 new 和 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/) 讓我們先回顧一下基礎。為什么有些人想要替換編譯器提供的 operator new 或 operator delete 版本呢?有三個最主要的原因: * **為了監測使用錯誤。**對由 new 產生的內存沒有實行 delete 會導致內存泄漏。在 new 出的內存上實行多于一次的 delete 會引發未定義行為。如果 operator new 保存一個已分配地址的列表,而 operator delete 從這個列表中移除地址,這樣就很容易監測到上述使用錯誤。同樣,某種編程錯誤會導致 data overruns(數據上溢)(在一個已分配塊的末端之后寫入)和 underruns(下溢)(在一個已分配塊的始端之前寫入)。在對于客戶可用的內存的之前和之后,自定義 operator news 可以跨越分配塊,在這些空間放置已知的字節模式 ("signatures")。operator deletes 會去檢查這些 signatures 是否依舊保持原樣。如果不是,在這個分配塊的生存期間的某個時刻發生了一個上溢或者下溢,而且 operator deletes 可以記錄這件事以及那個討厭的指針的值。 * **為了提升性能。**由編譯器加載的 operator new 和 operator delete 版本是為了多種用途而設計的。它們必須被長時間運行的程序(例如,web servers),接受,但是,它們也必須被運行時間少于一秒的程序接受。它們必須處理大內存塊,小內存塊,以及兩者混合的請求序列。它們必須適應廣泛的分配模式,從存在于整個程序的持續期間的少數幾個區塊的動態分配到大量短壽命 objects 的持續不斷的分配和釋放。它們必須為堆碎片化負責,對這個過程,如果不進行控制,最終會導致不能滿足對大內存塊的請求,即使有足夠的自由內存分布在大量小塊中。 > 由于內存管理器的特定需求,由編譯器加載的 operator news 和 operator deletes 采取了 middle-of-the-road strategy(中間路線策略)不值得大驚小怪。它們的工作對每一個人來說都說得過去,但是對誰都不是最合適的。如果你對你的程序的動態內存的應用模式有充分的理解,你可能經常發現 operator new 和 operator delete 的自定義版本勝于缺省版本。對于“勝于”,我的意思是它們運行更快——有時會有數量級的提升——而且它們需要更少的內存——最高會少于 50%。對于某些(盡管不意味著全部)應用程序,用自定義版本取代普通的 new 和 delete 是獲得重大性能提升的一個簡單方法。 * **為了收集使用方法的統計數據。**在一頭扎入編寫自定義 news 和 deletes 的道路之前,收集一下你的軟件如何使用動態內存的信息還是比較明智的。被分配區塊大小的分布如何?生存期的分布如何?它們的分配和釋放的順序是趨向于 FIFO ("first in, first out")(“先進先出”),或者 LIFO ("last in, first out")(“后進先出”)的順序,還是某種接近于隨機的順序?使用模式會隨著時間而變化嗎?例如,你的軟件是不是在不同的運行階段有不同的分配/釋放模式?在任一時間內使用中的動態分配內存的最大值(也就是說,它的“最高水位”)是多少?operator new 和 operator delete 的自定義版本使得收集這類信息變得容易。 在概念上,編寫一個自定義 operator new 相當簡單。例如,這是一個便于 under- 和 overruns 的檢測的 global operator new 的主要部分。這里有很多小麻煩,但是我們馬上就來關注一下它們。 ``` static const int signature = 0xDEADBEEF; typedef unsigned char Byte; // this code has several flaws—see below void* operator new(std::size_t size) throw(std::bad_alloc) { ? using namespace std; ? size_t realSize = size + 2 * sizeof(int);??? // increase size of request so2 ?????????????????????????????????????????????? // signatures will also fit inside ? void *pMem = malloc(realSize);?????????????? // call malloc to get theactual ? if (!pMem) throw bad_alloc();??????????????? // memory ? // write signature into first and last parts of the memory ? *(static_cast&lt;int*&gt;(pMem)) = signature; ? *(reinterpret_cast&lt;int*&gt;(static_cast&lt;Byte*&gt;(pMem)+realSize-sizeof(int))) = ? signature; ? // return a pointer to the memory just past the first signature ? return static_cast&lt;Byte*&gt;(pMem) + sizeof(int); } ``` 這個 operator new 的大多數缺陷都與它沒有遵循叫這個名字的函數的 C++ 慣例有關。例如,[Item 51](http://blog.csdn.net/fatalerror99/archive/2006/12/28/1466315.aspx) 闡明:所有的 operator new 都應該包含一個調用 new-handling function 的循環,但是這里沒有。但是,[Item 51](http://blog.csdn.net/fatalerror99/archive/2006/12/28/1466315.aspx) 正是專用于這樣的慣例,所以我在這里忽略它們。我現在要關注一個更微妙的問題:_alignment_(排列對齊)。 很多計算機架構要求特定類型的數據要放置在內存中具有特定性質的地址中。例如,一種架構可能要求 pointers(指針)要出現在四的倍數的地址上(也就是說,按照四字節對齊)或者 doubles(雙精度浮點型)必須出現在八的倍數的地址上(也就是說,按照八字節對齊)。不遵守這樣的約束會導致 hardware exceptions at runtime(運行時硬件異常)。其它的架構可能會寬容一些,但是如果滿足了排列對齊的次序會得到更好的性能。例如,在 Intel x86 架構上 doubles(雙精度浮點型)可以按照任意字節分界排列,但是如果他們按照八字節對齊,訪問速度會快得多。 alignment(排列對齊)在這里有重大意義,因為 C++ 要求所有的 operator news 返回適合任何數據類型的排列的指針。malloc 也工作于同樣的要求下,所以,讓 operator new 返回它從 malloc 得到的指針是安全的。然而,在上面的 operator news 中,我們沒有返回我們從 malloc 得到的指針,我們返回的指針比我們從 malloc 得到的指針偏移了一個 int 大小。無法保證這是安全的!如果客戶調用 operator new 為一個 double(或者,如果我們正在編寫 operator new[],一個 doubles 的數組)申請足夠的內存,而且我們正在運行一臺 ints 是四個字節大小而 doubles 需要八字節對齊的機器,我們就可能返回對齊不恰當的指針。這可以導致程序崩潰。或者,它只是導致運行速度變慢。無論哪種情況,這或許都不是我們想要的。 像 alignment(排列對齊)這樣的細節可以用于區分專業品質的內存管理器和那些由需要解決其它任務而心煩意亂的程序員匆匆拼湊出來的東西。編寫一個幾乎能工作的自定義內存管理器相當容易。編寫一個工作得很好的要困難得多。作為一個一般規則,我建議你不要致力于此,除非你不得不做。 很多情況下,你并非不得不做。有些編譯器提供選項開關用為它們的 memory management functions(內存管理函數)打開調試和記錄的功能。快速瀏覽一下你的編譯器的文檔也許可以打消你編寫 new 和 delete 的念頭。在很多平臺上,商用產品可以替代隨編譯器提供的 memory management functions(內存管理函數)。為了利用它們的增強的功能以及(或許會有的)更好的性能,你需要做的全部就是重新鏈接。(當然,你還必須把它們買回來。) 另一個選擇是開源的內存管理器。它們可用于多種平臺,所以你可以下載并試用。出自于 Boost(參見 Item 55)的 Pool library 就是一個這樣的開源分配器。Pool library 提供了針對自定義內存管理能提供幫助的最通常的情況之一(大數量 small objects(小對象)的分配)進行了調諧的分配器。很多 C++ 書籍,包括本書的早期版本,展示了一個 high-performance small-object allocator(高性能小對象分配器)的代碼,但是它們通常忽略了可移植性和排列對齊的考慮以及線程安全等等諸如此類的麻煩的細節。真正的庫會注意用健壯得多的代碼。即使你決定編寫你自己的 news 和 deletes,看一下開源版本很可能會為你提供對“區分幾乎起作用和真正起作用”的容易忽略的細節的洞察力。(已知 alignment(排列對齊)就是一個這樣的細節,值得一提的是,TR1(參見 Item 54)包含了對已發現的類型特定的排列對齊要求的支持。) 這個 Item 的主題是了解何時替換 new 和 delete 的缺省版本(無論是基于全局的還是 per-class 的)才有意義。我們現在應該比前面更詳細地總結一下時機問題。 * **為了監測使用錯誤**(如前)。 * **為了收集有關動態分配內存的使用的統計數據**(如前)。 * **為了提升分配和回收的速度。**general-purpose allocators(通用目的的分配器)通常(雖然不總是)比自定義版本慢很多,特別是如果自定義版本是為某種特定類型的 objects 專門設計的。class-specific allocators(類專用分配器)是 fixed-size allocators(固定大小分配器)(就像 Boost 的 Pool library 所提供的那些)的一種典范應用。如果你的程序是 single-threaded(單線程)的,而你的編譯器缺省的內存管理例程是 thread-safe(線程安全)的,通過編寫 thread-unsafe allocators(非線程安全分配器)你可以獲得相當的速度提升。當然,在得出 operator new 和 operator delete 對速度提升有價值的結論之前,確實測定你的程序以保證這些函數是真正的瓶頸。 * **為了減少缺省內存管理的空間成本。**general-purpose memory managers(通用目的的內存管理器)通常(雖然不總是)不僅比自定義版本慢,而且還經常使用更多的內存。這是因為它們經常為每個已分配區塊招致某些成本。針對 small objects(小對象)調諧的分配器(諸如 Boost 的 Pool library 中的那些)從根本上消除了這樣的成本。 * **為了調整缺省分配器不適當的排列對齊。**就像我前面提到的,在 x86 架構上,當 doubles 按照八字節對齊時訪問速度是最快的。哎呀,有些隨編譯器提供的 operator news 不能保證 doubles 的動態分配按照八字節對齊。在這種情況下,用保證按照八字節對齊的 operator new 替換掉缺省版本,可以使程序性能得到較大提升。 * **為了聚集相關的 objects,使它們彼此靠近。**如果你知道特定的 data structures(數據結構)通常會在一起使用,而且你想將在這些數據上工作時的頁錯誤頻率降到最低,那么為這些 data structures(數據結構)創建一個獨立的 heap(堆)以便讓它們盡可能地聚集在不多的幾個頁上就是有意義的。new 和 delete 的 placement versions(參見 [Item 52](http://blog.csdn.net/fatalerror99/archive/2007/01/21/1489466.aspx))使得完成這樣的聚集成為可能。 * **為了獲得不同尋常的行為。**有時你想讓 operators new 和 delete 做一些編譯器裝備版本沒有提供的事情。例如,你可能想在共享內存中分配和回收區塊,但是只能通過一個 C API 來管理那片內存。編寫 new 的 delete 的自定義版本(或許是 placement versions——再次參見 [Item 52](http://blog.csdn.net/fatalerror99/archive/2007/01/21/1489466.aspx))允許你用 C++ 衣服來遮住那個 C API。作為另一個例子,你可以編寫一個自定義的 operator delete 用 zeros 復寫被回收的內存以提高應用程序數據的安全性。 **Things to Remember** * 有很多正當的編寫 new 和 delete 的自定義版本的理由,包括改進性能,調試 heap(堆)用法錯誤,以及收集 heap(堆)用法信息。
                  <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>

                              哎呀哎呀视频在线观看