C++ RAII
為什么80%的碼農(nóng)都做不了架構(gòu)師?>>> ??
C++?RAII
????RAII是resource?acquisition?is?initialization的縮寫(xiě),意為“資源獲取即初始化”。它是C++之父Bjarne?Stroustrup提出的設(shè)計(jì)理念,其核心是把資源和對(duì)象的生命周期綁定,對(duì)象創(chuàng)建獲取資源,對(duì)象銷(xiāo)毀釋放資源。在RAII的指導(dǎo)下,C++把底層的資源管理問(wèn)題提升到了對(duì)象生命周期管理的更高層次。
????說(shuō)起來(lái),RAII的含義倒也不算復(fù)雜。用白話(huà)說(shuō)就是:在類(lèi)的構(gòu)造函數(shù)中分配資源,在析構(gòu)函數(shù)中釋放資源。這樣,當(dāng)一個(gè)對(duì)象創(chuàng)建的時(shí)候,構(gòu)造函數(shù)會(huì)自動(dòng)地被調(diào)用;而當(dāng)這個(gè)對(duì)象被釋放的時(shí)候,析構(gòu)函數(shù)也會(huì)被自動(dòng)調(diào)用。于是乎,一個(gè)對(duì)象的生命期結(jié)束后將會(huì)不再占用資源,資源的使用是安全可靠的。
C++?RAII體現(xiàn)出了簡(jiǎn)潔、安全、實(shí)時(shí)的特點(diǎn):
1.概念簡(jiǎn)潔性:讓資源(包括內(nèi)存和非內(nèi)存資源)和對(duì)象的生命周期綁定,資源類(lèi)的設(shè)計(jì)者只需用在類(lèi)定義內(nèi)部處理資源問(wèn)題,提高了程序的可維護(hù)性
2.類(lèi)型安全性:通過(guò)資源代理對(duì)象包裝資源(指針變量),并利用運(yùn)算符重載提供指針運(yùn)算方便使用,但對(duì)外暴露類(lèi)型安全的接口
3.異常安全性:棧語(yǔ)義保證對(duì)象析構(gòu)函數(shù)的調(diào)用,提高了程序的健壯性
4.釋放實(shí)時(shí)性:和GC相比,RAII達(dá)到了和手動(dòng)釋放資源一樣的實(shí)時(shí)性,因此可以承擔(dān)底層開(kāi)發(fā)的重任
也許你還在驚訝RAII如此簡(jiǎn)單的時(shí)候,關(guān)于RAII的主要內(nèi)容已經(jīng)介紹完了。簡(jiǎn)單不意味著簡(jiǎn)陋,在我看來(lái)RAII雖然不像GC一樣,是一套具體的機(jī)制,但它蘊(yùn)含的對(duì)象與資源關(guān)系的哲學(xué)深度的理解卻使得我對(duì)Bjarne?Stroustrup肅然起敬!
最后,不得不提醒RAII的理念固然簡(jiǎn)單,不過(guò)在具體實(shí)現(xiàn)的時(shí)候仍有需要小心的地方。比如對(duì)于STL的auto_ptr,可以視為資源的代理對(duì)象,auto_ptr對(duì)象間的賦值是一個(gè)需要特別注意的地方。簡(jiǎn)單說(shuō)來(lái)資源代理對(duì)象間賦值的語(yǔ)義不滿(mǎn)足“賦值相等”,其語(yǔ)義是資源管理權(quán)的轉(zhuǎn)移。
什么是“賦值相等”呢?比如:
int?a;??int?b?=?10;??a?=?b;?//這句話(huà)執(zhí)行后?a?==?b?但對(duì)于資源代理對(duì)象,這是不滿(mǎn)足的,比如:
auto_ptr<int>?a(null);??auto_ptr<int>?b(new?int(123));??a?=?b;?//這句話(huà)執(zhí)行后a?!=?b,賦值的語(yǔ)義是b把資源的管理權(quán)交給了a?
auto_ptr是這樣一種指針:它是“它所指向的對(duì)象”的擁有者。這種擁有具有唯一性,即一個(gè)對(duì)象只能有一個(gè)擁有者,嚴(yán)禁一物二主。當(dāng)auto_ptr指針被摧毀時(shí),它所指向的對(duì)象也將被隱式銷(xiāo)毀,即使程序中有異常發(fā)生,auto_ptr所指向的對(duì)象也將被銷(xiāo)毀。
關(guān)于auto_ptr的幾種注意事項(xiàng):
1、auto_ptr不能共享所有權(quán)。
2、auto_ptr不能指向數(shù)組
3、auto_ptr不能作為容器的成員。
4、不能通過(guò)賦值操作來(lái)初始化auto_ptr
std::auto_ptr<int>?p(new?int(42));?????//OK
std::auto_ptr<int>?p?=?new?int(42);????//ERROR
這是因?yàn)閍uto_ptr?的構(gòu)造函數(shù)被定義為了explicit
5、不要把a(bǔ)uto_ptr放入容器
下面便是在C++中實(shí)現(xiàn)RAII的典型代碼:
class?file
{
public:
????file(string?const&?name)?{
????????m_fileHandle=fopen(name.cstr());
????}
????~file()?{
????????fclose(m_fileHandle);
????}
????//
private:
????handle?m_fileHandle;
}
很典型的“在構(gòu)造函數(shù)里獲取,在析構(gòu)函數(shù)里釋放”。如果我寫(xiě)下代碼:???
void?fun1()?{
????file?myfile("my.txt");
?????//操作文件
}?//此處銷(xiāo)毀對(duì)象,調(diào)用析構(gòu)函數(shù),釋放資源
當(dāng)函數(shù)結(jié)束時(shí),局部對(duì)象myfile的生命周期也結(jié)束了,析構(gòu)函數(shù)便會(huì)被調(diào)用,資源會(huì)得到釋放。而且,如果函數(shù)中的代碼拋出異常,那么析構(gòu)函數(shù)也會(huì)被調(diào)用,資源同樣會(huì)得到釋放。所以,在RAII下,不僅僅資源安全,也是異常安全的。
但是,在如下的代碼中,資源不是安全的,盡管我們實(shí)現(xiàn)了RAII:
void?fun2()?{
????file?pfile=new?file("my.txt");
?????//操作文件
}
因?yàn)槲覀冊(cè)诙焉蟿?chuàng)建了一個(gè)對(duì)象(通過(guò)new),但是卻沒(méi)有釋放它。我們必須運(yùn)用delete操作符顯式地加以釋放:
void?fun3()?{
????file?pfile=new?file("my.txt");
?????//操作文件
????????delete?pfile;
}
否則,非但對(duì)象中的資源得不到釋放,連對(duì)象本身的內(nèi)存也得不到回收。(將來(lái),C++的標(biāo)準(zhǔn)中將會(huì)引入GC(垃圾收集),但正如下面分析的那樣,GC依然無(wú)法確保資源的安全)。
現(xiàn)在,在fun3(),資源是安全的,但卻不是異常安全的。因?yàn)橐坏┖瘮?shù)中拋出異常,那么delete?pfile;這句代碼將沒(méi)有機(jī)會(huì)被執(zhí)行。
C++領(lǐng)域的諸位大牛們告誡我們:如果想要在沒(méi)有GC的情況下確保資源安全和異常安全,那么請(qǐng)使用智能指針:
void?fun4()?{
????auto_ptr<file>?spfile(new?file("my.txt"));
?????//操作文件
}?//此處,spfile結(jié)束生命周期的時(shí)候,會(huì)釋放(delete)對(duì)象
那么,智能指針又是怎么做到的呢?下面的代碼告訴你其中的把戲(關(guān)于智能指針的更進(jìn)一步的內(nèi)容,請(qǐng)參考std::auto_ptr,boost或shared_ptr的智能指針)。
也就是說(shuō),智能指針通過(guò)RAII來(lái)確保內(nèi)存資源的安全,也間接地使得對(duì)象上的RAII得到實(shí)施。不過(guò),這里的RAII并不是十分嚴(yán)格:對(duì)象(所占的內(nèi)存也是資源)的創(chuàng)建(資源獲取)是在構(gòu)造函數(shù)之外進(jìn)行的。廣義上,我們也把它劃歸RAII范疇。
但是,Matthew?Wilson在《Imperfect?C++》一書(shū)中,將其獨(dú)立出來(lái),稱(chēng)其為RRID(Resource?Release?Is?Destruction)。
RRID的實(shí)施需要在類(lèi)的開(kāi)發(fā)者和使用者之間建立契約,采用相同的方法獲取和釋放資源。比如,如果在shared_ptr構(gòu)造時(shí)使用malloc(),便會(huì)出現(xiàn)問(wèn)題,因?yàn)閟hared_ptr是通過(guò)delete釋放對(duì)象的。
轉(zhuǎn)載于:https://my.oschina.net/mskk/blog/3004171
總結(jié)
- 上一篇: git 远程分支和tag标签的操作
- 下一篇: 实现数组类(C++ 拷贝构造函数、拷贝函