3 回答

TA貢獻1789條經(jīng)驗 獲得超8個贊
出于多種原因,人們可能會嘗試替換new
和delete
操作符,即:
要檢測使用錯誤:
有多種方法會導(dǎo)致錯誤使用new
和delete
可能導(dǎo)致未定義行為和內(nèi)存泄漏的可怕野獸。每種示例分別為:在ed內(nèi)存上
使用多個內(nèi)存,而不在使用分配的內(nèi)存上調(diào)用。 重載的運算符可以保留分配的地址列表,而重載的運算符可以從列表中刪除地址,因此很容易檢測到此類使用錯誤。delete
new
delete
new
new
delete
同樣,各種編程錯誤也可能導(dǎo)致數(shù)據(jù)溢出(超出分配的塊的末尾寫入)和欠載(超出分配的塊的開始前寫入)。
重載運算符new
可能會過度分配塊,并在內(nèi)存可供客戶端使用之前和之后放置已知的字節(jié)模式(“簽名”)。重載的運算符刪除操作可以檢查簽名是否仍然完整。因此,通過檢查這些簽名是否完好無損,可以確定在分配的塊的有效期內(nèi)某個時間發(fā)生了超限運行或欠載運行,操作員刪除可以將該事實以及有問題的指針的值記錄下來,從而有助于提供良好的診斷信息。
要提高效率(速度和內(nèi)存):
該new
和delete
為大家運營商合作得相當(dāng)好,但最佳的人。此行為是由于它們僅設(shè)計用于通用目的而引起的。它們必須適應(yīng)各種分配模式,從在程序運行期間存在的幾個塊的動態(tài)分配到大量短期對象的持續(xù)分配和重新分配。最終,運算符new
和delete
編譯器附帶的運算符采取了中間策略。
如果您對程序的動態(tài)內(nèi)存使用模式有很好的了解,則通常會發(fā)現(xiàn)自定義版本的operator new和operator delete性能優(yōu)于默認版本(性能更快,或需要的內(nèi)存最多減少50%)。當(dāng)然,除非您確定自己在做什么,否則這樣做不是一個好主意(如果您不了解所涉及的復(fù)雜性,甚至不要嘗試這樣做)。
收集使用情況統(tǒng)計信息:
如#2所述,在考慮替換new
并delete
提高效率之前,您應(yīng)該收集有關(guān)應(yīng)用程序/程序如何使用動態(tài)分配的信息。您可能需要收集以下信息:
分配塊的
分配,生存期的分配,分配的
順序(FIFO或LIFO或隨機的),
了解一段時間內(nèi)使用模式的變化,所使用的最大動態(tài)內(nèi)存等。
另外,有時您可能需要收集使用情況信息,例如:
計算類的動態(tài)對象
數(shù),限制使用動態(tài)分配創(chuàng)建的對象數(shù)等。
所有這些信息都可以通過替換習(xí)慣new
并delete
在重載的new
和中添加診斷收集機制來收集delete
。
為了補償次優(yōu)的內(nèi)存對齊方式new
:
許多計算機體系結(jié)構(gòu)要求將特定類型的數(shù)據(jù)放置在內(nèi)存中特定種類的地址處。例如,體系結(jié)構(gòu)可能要求指針出現(xiàn)在四個整數(shù)倍的地址(即四字節(jié)對齊),或者雙精度必須出現(xiàn)在八個整數(shù)倍的地址(即八字節(jié)對齊)。不遵守這些約束條件可能會導(dǎo)致在運行時出現(xiàn)硬件異常。其他架構(gòu)則更為寬容,盡管降低了性能new
,但仍可能允許其工作。某些編譯器附帶的運算符不保證雙精度字節(jié)的動態(tài)分配的八字節(jié)對齊。在這種情況下,替換默認運算符new
保證八字節(jié)對齊的程序可以大大提高程序性能,并且可能是替換new
和delete
操作員的好理由。
要將相關(guān)對象彼此聚集在一起:
如果您知道特定的數(shù)據(jù)結(jié)構(gòu)通常一起使用,并且希望最大程度地減少處理數(shù)據(jù)時出現(xiàn)頁面錯誤的頻率,那么為數(shù)據(jù)結(jié)構(gòu)創(chuàng)建一個單獨的堆是有意義的,因此可以將它們聚集在很少的數(shù)量上頁面。的自定義展示位置版本,new
并且delete
可以實現(xiàn)此類聚類。
要獲得非常規(guī)行為:
有時,您希望操作員是新的并且要刪除,以執(zhí)行編譯器提供的版本不提供的操作。
例如:您可能編寫了一個自定義運算符delete
,該運算符用零覆蓋釋放的內(nèi)存,以提高應(yīng)用程序數(shù)據(jù)的安全性。

TA貢獻1847條經(jīng)驗 獲得超11個贊
首先,確實有許多不同的new
和delete
運算符(確實是一個任意數(shù))。
首先,有::operator new
,::operator new[]
,::operator delete
和::operator delete[]
。其次,對于任何一類X
,有X::operator new
,X::operator new[]
,X::operator delete
和X::operator delete[]
。
在這兩者之間,重載特定于類的運算符比全局運算符更為常見-特定類的內(nèi)存使用遵循足夠特定的模式是很常見的,您可以編寫可以對默認值進行實質(zhì)性改進的運算符。通常很難準(zhǔn)確地或?qū)iT地在全局范圍內(nèi)預(yù)測內(nèi)存使用情況。
可能還值得一提的是,盡管operator new
和operator new[]
彼此分開(對于X::operator new
和X::operator new[]
,也是如此),但兩者的要求之間沒有區(qū)別。將調(diào)用一個分配一個對象,另一個分配一個對象數(shù)組,但每個對象仍只接收所需的內(nèi)存量,并且需要返回(至少)那么大的內(nèi)存塊的地址。
說到需求,它可能是值得檢討的其他要求1:全球運營商必須是真正的全球性-你可以不把一個命名空間內(nèi)或使一個靜態(tài)的特定翻譯單元。換句話說,只有兩個級別可以發(fā)生重載:特定于類的重載或全局重載。不允許使用諸如“名稱空間X中的所有類”或“翻譯單元Y中的所有分配”之類的中間點。特定于類的運算符必須是static
-但實際上并不需要將它們聲明為靜態(tài)- 無論您是否顯式聲明它們,它們都將是靜態(tài)的static
或不。正式地講,全局運算符會返回許多對齊的內(nèi)存,以便可以將其用于任何類型的對象。非正式地,在一個方面有一個小小的擺動空間:如果您請求一個小塊(例如2個字節(jié)),您實際上只需要為最大大小的對象提供對齊的內(nèi)存,因為嘗試在該處存儲更大的對象無論如何會導(dǎo)致不確定的行為。
在介紹了這些預(yù)備知識之后,讓我們回到有關(guān)為什么要重載這些運算符的原始問題。首先,我應(yīng)該指出,重載全局運算符的原因與重載特定于類的運算符的原因有很大的不同。
由于它比較常見,因此我將首先討論特定于類的運算符。特定于類的內(nèi)存管理的主要原因是性能。這通常以兩種形式中的一種(或兩種)出現(xiàn):提高速度或減少碎片。內(nèi)存管理器僅處理特定大小的塊,從而提高了速度,因此它可以返回任何空閑塊的地址,而不用花費任何時間檢查一個塊是否足夠大,如果將塊分成兩個碎片的減少((大多數(shù))以相同的方式減少)-例如,為N個對象預(yù)分配足夠大的塊,這恰好為N個對象提供了所需的空間;分配一個對象的內(nèi)存將完全分配一個對象的空間,而不是一個字節(jié)。
使全局內(nèi)存管理運算符超載的原因有很多。其中許多方法面向調(diào)試或檢測,例如跟蹤應(yīng)用程序所需的總內(nèi)存(例如,為移植到嵌入式系統(tǒng)做準(zhǔn)備),或通過顯示分配和釋放內(nèi)存之間的不匹配來調(diào)試內(nèi)存問題。另一個常見的策略是在每個請求的塊的邊界之前和之后分配額外的內(nèi)存,并將唯一的模式寫入這些區(qū)域。在執(zhí)行結(jié)束時(可能還有其他時間),將檢查這些區(qū)域,以查看代碼是否已寫入分配的邊界之外。另一嘗試是通過使存儲器分配或刪除的至少某些方面自動化來嘗試提高易用性,例如利用自動化垃圾收集器。。
也可以使用非默認全局分配器來提高性能。典型的情況是替換通常速度較慢的默認分配器(例如,至少4.x左右的某些版本的MS VC ++ 將為每個分配/刪除操作調(diào)用系統(tǒng)HeapAlloc
和HeapFree
函數(shù))。我在實踐中看到的另一種可能性是使用SSE操作時發(fā)生在Intel處理器上。它們對128位數(shù)據(jù)進行操作。盡管無論對齊方式如何操作都可以工作,但是將數(shù)據(jù)對齊到128位邊界時,速度得到了提高。一些編譯器(例如MS VC ++ 2))并不一定要與更大的邊界對齊,因此即使使用默認分配器的代碼可以工作,替換分配也可以大大提高這些操作的速度。
C ++標(biāo)準(zhǔn)的第3.7.3節(jié)和第18.4節(jié)(或C ++ 0x的第3.7.4節(jié)和第18.6節(jié),至少從N3291開始)涵蓋了大多數(shù)要求。
我感到有必要指出,我不打算選擇Microsoft的編譯器-我懷疑它會出現(xiàn)異常數(shù)量的此類問題,但是我經(jīng)常使用它,因此我傾向于非常了解它的問題。

TA貢獻1864條經(jīng)驗 獲得超6個贊
許多計算機體系結(jié)構(gòu)要求將特定類型的數(shù)據(jù)放置在內(nèi)存中特定種類的地址處。例如,體系結(jié)構(gòu)可能要求指針出現(xiàn)在四個整數(shù)倍的地址(即四字節(jié)對齊),或者雙精度必須出現(xiàn)在八個整數(shù)倍的地址(即八字節(jié)對齊)。不遵守這些約束條件可能會導(dǎo)致在運行時出現(xiàn)硬件異常。其他體系結(jié)構(gòu)則更為寬容,盡管降低了性能,也可能使其工作。
需要說明的是:如果某個架構(gòu)要求例如將double數(shù)據(jù)按8字節(jié)對齊,則沒有什么要優(yōu)化的。任何種類的適當(dāng)大小的動態(tài)分配的(例如malloc(size),operator new(size),operator new[](size),new char[size]其中size >= sizeof(double))是保證適當(dāng)對準(zhǔn)。如果一個實現(xiàn)不能保證這一點,那就不符合要求。operator new在這種情況下,更改為執(zhí)行“正確的事情”將是嘗試“修復(fù)”實現(xiàn),而不是優(yōu)化。
另一方面,某些體系結(jié)構(gòu)允許一種或多種數(shù)據(jù)類型的不同(或所有)對齊方式,但根據(jù)那些相同類型的對齊方式,提供不同的性能保證。然后,實現(xiàn)可以返回次優(yōu)對齊且仍然符合要求的內(nèi)存(再次假設(shè)為適當(dāng)大小的請求)。這就是該示例的含義。
- 3 回答
- 0 關(guān)注
- 645 瀏覽
添加回答
舉報