“伊凡 C普”第一式-内存管理
<條款5>對應(yīng)的new和delete要采用相同的形式。
這一條沒什么可說的,注意new的是不是數(shù)組就可以了。其中有兩點需要注意:
1.在寫一個包含指針數(shù)據(jù)成員,并且提供多個構(gòu)造函數(shù)的類時,構(gòu)造中new的形勢要相同,否則析構(gòu)中的delete便"為難"了。
2.喜歡用typedef的人需要注意,最好杜絕對數(shù)組類型用typedefs。
=====================================
<條款6>析構(gòu)函數(shù)里對指針成員調(diào)用delete
增加一個指針成員意味著幾乎都要進行下面的工作:
- 在每個構(gòu)造函數(shù)里對指針進行初始化。對于一些構(gòu)造函數(shù),如果沒有內(nèi)存要分配給指針的話,指針要被初始化為0(即空指針)。
- 刪除現(xiàn)有的內(nèi)存,通過賦值操作符分配給指針新的內(nèi)存。
- 在析構(gòu)函數(shù)里刪除指針。
=====================================
<條款7>預(yù)先準備好內(nèi)存不夠的情況
這一條款說的是:new可能調(diào)用失敗,你要做好準備工作。
一、與C中類似的方法-宏:
#definenew(ptr, type)?????????? /
try {(ptr) = new type; }?????? /
catch(std::bad_alloc&) { assert(0); }
此種方法缺點有2:
(1) 只是在沒定義標準宏ndebug的時候,即在調(diào)試狀態(tài)下才有效。
(2) 沒有考慮到new有各種各樣的使用方式。
二、當(dāng)內(nèi)存分配請求不能滿足時,調(diào)用你預(yù)先指定的一個出錯處理函數(shù)。
此方法基于一種機制:即當(dāng)operator new不能滿足請求時,會在拋出異常之前調(diào)用客戶指定的一個出錯處理函數(shù)。
指定出錯處理函數(shù)時要用到set_new_handler函數(shù),它的輸入?yún)?shù)是operator new分配內(nèi)存失敗時要調(diào)用的出錯處理函數(shù)的指針,返回值是set_new_handler沒調(diào)用之前就已經(jīng)在起作用的舊的出錯處理函數(shù)的指針(有點類似GDI中設(shè)置dc的畫筆)。下面是一個使用set_new_handler的例子。
//function to call if operator new can't allocateenough memory
void nomorememory()
{
cerr<< "unable to satisfy request for memory/n";
abort();
}
int main()
{
set_new_handler(nomorememory);
int*pbigdataarray = new int[100000000];
...... ...
}
operator new不能滿足內(nèi)存分配請求時,new-handler函數(shù)不只調(diào)用一次,而是不斷重復(fù),直至找到足夠的內(nèi)存(里面有一個while(1)循環(huán))。
一個設(shè)計得好的new-handler函數(shù)必須實現(xiàn)下面功能中的一種:
<1>產(chǎn)生更多的可用內(nèi)存。這將使operator new下一次分配內(nèi)存的嘗試有可能獲得成功。實施這一策略的一個方法是:在程序啟動時分配一個大的內(nèi)存塊,然后在第一次調(diào)用new-handler時釋放。釋放時伴隨著一些對用戶的警告信息,如內(nèi)存數(shù)量太少,下次請求可能會失敗,除非又有更多的可用空間。
<2>安裝另一個不同的new-handler函數(shù)。如果當(dāng)前的new-handler函數(shù)不能產(chǎn)生更多的可用內(nèi)存,可能它會知道另一個new-handler函數(shù)可以提供更多的資源。這樣的話,當(dāng)前的new-handler可以安裝另一個new-handler來取代它(通過調(diào)用set_new_handler)。下一次operator new調(diào)用new-handler時,會使用最近安裝的那個。(這一策略的另一個變通辦法是讓new-handler可以改變它自己的運行行為,那么下次調(diào)用時,它將做不同的事。方法是使new-handler可以修改那些影響它自身行為的靜態(tài)或全局數(shù)據(jù)。)
<3>卸除new-handler,拋出標準異常。也就是傳遞空指針給set_new_handler。沒有安裝new-handler,operator new分配內(nèi)存不成功時就會拋出一個標準的std::bad_alloc類型的異常。
<4>拋出std::bad_alloc或從std::bad_alloc繼承的其他類型的異常。這樣的異常不會被operator new捕捉,所以它們會被送到最初進行內(nèi)存請求的地方。(拋出別的不同類型的異常會違反operator new異常規(guī)范。規(guī)范中的缺省行為是調(diào)用abort,所以new-handler要拋出一個異常時,一定要確信它是從std::bad_alloc繼承來的。想更多地了解異常規(guī)范,參見條款m14。)
<5>沒有返回,直接中斷退出。典型做法是調(diào)用abort或exit。abort/exit可以在標準c庫中找到(還有標準c++庫,參見條款49)。
二、寫類自己的new和set_new_handler
簡言之,分三步:1.class里面的"三樣"要齊全。
(1) set_new_handler
(2) operator new
(3) static new_handler currenthandler;
2.實現(xiàn)set_new_handler。
(1) 保存?zhèn)鹘o它的任何指針,并返回在調(diào)用它之前所保存的任何指針。
(2) 保存?zhèn)鹘o它的任何指針,并返回在調(diào)用它之前所保存的任何指針。
3.實現(xiàn)operator new。
(1) 調(diào)用標準set_new_handler函數(shù),輸入?yún)?shù)為x的出錯處理函數(shù)。這使得x的new-handler函數(shù)成為全局new-handler函數(shù)。注意下面的代碼中,用了"::"符號顯式地引用std空間(標準set_new_handler函數(shù)就存在于std空間)。
(2) 調(diào)用全局operatornew分配內(nèi)存。如果第一次分配失敗,全局operator new會調(diào)用x的new-handler,因為它剛剛(見1.)被安裝成為全局new-handler。如果全局operator new最終未能分配到內(nèi)存,它拋出std::bad_alloc異常,x的operator new會捕捉到它。x的operator new然后恢復(fù)最初被取代的全局new-handler函數(shù),最后以拋出異常返回。
(3) 假設(shè)全局operatornew為類型x的對象分配內(nèi)存成功,, x的operator new會再次調(diào)用標準set_new_handler來恢復(fù)最初的全局出錯處理函數(shù)。最后返回分配成功的內(nèi)存的指針。
c++是這么做的。
下面是一個"混合風(fēng)格"(mixin-style)的基類,這種基類允許子類繼承特定的功能:建立一個類的new-handler的功能。
template<classt> // 提供類set_new_handler支持的
classnewhandlersupport {???? // 混合風(fēng)格"的基類
public:
static new_handlerset_new_handler(new_handler p);
static void * operator new(size_tsize);
private:
static new_handler currenthandler;
};
template<classt>
new_handlernewhandlersupport<t>::set_new_handler(new_handler p)
{
new_handler oldhandler =currenthandler;
currenthandler = p;
return oldhandler;
}
template<classt>
void *newhandlersupport<t>::operator new(size_t size)
{
new_handler globalhandler =
std::set_new_handler(currenthandler);
void *memory;
try {
memory = ::operatornew(size);
}
catch (std::bad_alloc&) {
std::set_new_handler(globalhandler);
throw;
}
std::set_new_handler(globalhandler);
return memory;
}
// this sets eachcurrenthandler to 0
template<class t>
new_handlernewhandlersupport<t>::currenthandler;
=====================================
?
<條款8>寫operator new和operator delete時要遵循常規(guī)
一句話,系統(tǒng)缺省怎么做,你就怎么做。
一、new所要做的
1)要有正確的返回值;
2)可用內(nèi)存不夠時要調(diào)用出錯處理函數(shù)(見條款7);
3)處理好0字節(jié)內(nèi)存請求的情況;
4)避免不小心隱藏了標準形式的new(不過這是條款9的話題)。
這樣, operator new的偽代碼看起來會象下面這樣:
void * operator new(size_t size)??????????// operator new還可能有其它參數(shù)
{??????????????????????????????????????
if (size != sizeof(base))???????????????????? // 如果數(shù)量"錯誤",讓標準operator new
return ::operator new(size);????????????// 去處理這個請求????????????????????????????
???? while (1) {
分配size字節(jié)內(nèi)存;
if (分配成功)
??????return (指向內(nèi)存的指針);
// 分配不成功,找出當(dāng)前出錯處理函數(shù)
????new_handler globalhandler = set_new_handler(0);
set_new_handler(globalhandler);
if (globalhandler) (*globalhandler)();
else throw std::bad_alloc();
??}
}
三、delete的任務(wù)
所要記住的只是,c++保證刪除空指針永遠是安全的,所以你要充分地應(yīng)用這一保證。
偽代碼如下:
void base::operator delete(void *rawmemory, size_t size)
{
??if (rawmemory == 0) return;?????????????????? // 檢查空指針
??if (size != sizeof(base)) {???????????????? // 如果size"錯誤",
::operator delete(rawmemory);????????// 讓標準operator來處理請求
return;????????????????????????
??}
??釋放指向rawmemory的內(nèi)存;
??return;
}
=====================================
<條款9>避免隱藏標準形式的new
在類里定義了一個稱為"operator new"的函數(shù)后,會不經(jīng)意地阻止了對標準new的訪
問。條款50解釋了為什么會這樣,這里我們更關(guān)心的是如何想個辦法避免這個問題。
一個辦法是在類里寫一個支持標準new調(diào)用方式的operator new,它和標準new做同樣的事。這可以用一個高效的內(nèi)聯(lián)函數(shù)來封裝實現(xiàn)。
另一種方法是為每一個增加到operator new的參數(shù)提供缺省值(見條款24)。
=====================================
<條款10>如果寫了operator new就要同時寫operator delete
問題1:為什么自己寫operator new 和 operator delete.(答:為了效率)
問題2:為什么要為自己的new寫自己的delete.(答:因為標準delete不知道自己new出了多大內(nèi)存,見《深度探索c++對象模型》)
問題3:內(nèi)存池.(詳情見eff??c++)
?
?
總結(jié)
以上是生活随笔為你收集整理的“伊凡 C普”第一式-内存管理的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 大数据实践总结分享
- 下一篇: 大气湍流下的少模光纤耦合