本原則是針對在多線程環(huán)境下內(nèi)存中動態(tài)存儲區(qū)也就是堆中空間資源的訪問而產(chǎn)生的,因為堆屬于全局性資源,每個線程都試圖去使用它,又因為每個線程是互斥地使用資源,那自然就會存在一個先到先得的問題,這就是所謂的競速。你要知道堆空間也是有限的,于是就可能出現(xiàn)當某個線程是要使用堆空間時,堆卻沒有足夠的空間提供給這個線程使用的時候。這個時候你再使用new來分配空間就會失敗。雖然C++中提供了默認的處理過程,但是有的時候你就是希望自定義整個new的過程,那該如何是好?本原則就是來幫你解決這個問題的。
在這里你要知道C++是如何處理動態(tài)內(nèi)存分配不足的問題的。以前如果遇到這種情況new會直接返回一個null,現(xiàn)在一般是返回一個bad_alloc異常。C++處理new異常通常有一個指向錯誤處理函數(shù)的函數(shù)指針new-handler,它負責對new分配不成功的處理。還有一個使用這個函數(shù)指針進行異常邏輯處理的函數(shù)叫set_new_handler,它們都在new頭文件里面。
有趣的是這個set_new_handler是以new-handler為參數(shù)并以new-handler為返回值的,那又是怎么回事呢?作為參數(shù)的這個new-handler是指new分配錯誤時被調(diào)用的錯誤處理函數(shù),注意!這個處理函數(shù)不一定非要是系統(tǒng)的庫函數(shù),它也可以是用戶自定義的,不過因為new-handler是一個無參數(shù)無返回值的函數(shù)的指針,所以這個錯誤處理函數(shù)也必須是這樣的一個函數(shù)。而那個返回的new-handler指向的是set_new_handler被調(diào)用前正在被執(zhí)行的new-handler類型函數(shù)。在這里我對我自己的理解表示不確定。
這樣只要new分配不成功它就不斷地接受新的new-handler返回舊的new-handler知道new能分配成功。這樣,設(shè)計一個良好的new-handler必須做到以下幾點:
1、讓可用內(nèi)存更多。這一點首先要做到有足夠的可供分配的內(nèi)存空間,其次就是每次調(diào)用new-handler的時候就要及時釋放掉那些沒有分配成功的內(nèi)存空間;
2、安裝另一個new-handler。用更強大的new-handler替換當前的new-handler;
3、卸載new-handler。如果沒有可用的new-handler,就將參數(shù)new-handler置為null使異常拋出;
4、拋出bad_alloc異常;
5、直接abort或者exit結(jié)束,啥也不做。
對于實現(xiàn)自定義new-handler和set_new_handler,作者的思想是在自定義接口中去調(diào)用這些系統(tǒng)函數(shù)來完成自定義功能,具體來說你就是在類內(nèi)實現(xiàn)就好了。在此作者給了個例子:

從上圖可見,主要就是在類中自定義兩個公有的static接口,這樣每個類的實例都可以使用這倆接口。其中一個是new的重載函數(shù),另一個是set-new-handler。它還有一個私有的static的new-handler類型的指針,用來set-new-handler被調(diào)用前正在被使用的new-handler函數(shù)。這個set-new-handler所做的事情就是先把當前正在運行的new-handler函數(shù)保存起來,然后更改currentHandler的指向為新的new-handler,最后返回那個保存起來的new-handler。
這個被重載的new主要完成以下工作:
Step 1、在發(fā)生錯誤的時候調(diào)用標準的set-new-handler,這會將set-new-handler的參數(shù)置為新的global-new-handler,我想這個global-new-handler是系統(tǒng)提供的new-handler吧;
Step 2、global new會被調(diào)用,我想它也就是系統(tǒng)提供的那個new吧。如果new失敗了,那么這個new會去調(diào)用類中的new-handler,因為由Step 1可知此時的new-handler實際上是系統(tǒng)的new-handler。如果這個系統(tǒng)的new-handler還是無法解決問題,它只能拋出bad-alloc異常。不過在這之前,類中的new必須把原來的系統(tǒng)的new-handler作為類中的set-new-handler的返回值來恢復(fù)系統(tǒng)的new-handler。為了先前的new-handler能得到恢復(fù),類必須把先前的new-handler作為資源來管理;
Step 3、如果系統(tǒng)的new能分配成功,那么類中的new就應(yīng)該返回該內(nèi)存的指針,析構(gòu)函數(shù)必須把先前的new-handler恢復(fù)回去。
因為這個方案并不因類的不同而不同,所以可以考慮把它做成父類的模版,讓各個子類去繼承這個特性,于是在這里作者提到了奇異遞歸模版模式,簡稱CRTP(Curiously Recurring Template Pattern)。它的樣子大致如下。

可以看出一個父類是以子類類型來進行特化的。
文中還提到為了支持以前在舊規(guī)范時候?qū)懗鰜淼拇a,C++標準委員會提供了另一種new它在失敗時直接返回null,它被稱為nothrow形式的new。其使用形式如下:

這種new不好,因為它具有局限性,它只能管住當前代碼后續(xù)代碼它就管不了了,所以相比之下還是拋出異常的new比較好。
總結(jié):
1、作為set-new-handler函數(shù)中的參數(shù)的new-handler允許用戶自定義,它在內(nèi)存分配不足時被調(diào)用。
2、Nothrow new太局限,它只適合于一段代碼,對于后續(xù)的構(gòu)造函數(shù)什么的拋出的異常卻束手無策。