用RAII技术管理资源及其泛型实现
前言
RAII的含義是“資源獲取即初始化”。
一段看似安全的代碼
首先看一段代碼:
這段代碼中,我們先進行了動態(tài)內(nèi)存分配,使用完釋放,看起來很完美,但是這段程序是否真的保證不會發(fā)生內(nèi)存泄漏?
考慮這樣一種情形,程序在使用這段內(nèi)存的過程中throw一個異常,于是程序轉(zhuǎn)向catch塊,然后XXX。 這段內(nèi)存被釋放了嗎? 顯然沒有。 那么這段程序應(yīng)該從哪里改進呢?
對象的生命期
考慮一個問題:C++中對象的聲明期是怎樣的?在C++中,對象創(chuàng)建的方式有兩種,一種是棧上,一種是堆上創(chuàng)建。
上面的代碼中,第一個對象創(chuàng)建在棧上,更明確的說法是它是一個局部變量,這意味著它的生命期起源于被創(chuàng)建的這行語句,終結(jié)與所在作用域的末尾,也就是這里的右花括號(})。
而第二個對象呢?它是采用所謂的動態(tài)內(nèi)存分配生成的,需要程序員手工去釋放,當調(diào)用delete的時候才銷毀,但調(diào)用delete的時機不是固定的。
也就是說,棧對象的生命期是明確的,而堆對象的生命期由于取決于調(diào)用delete的時機,因而是不明確的。
看到這里,之前那段有可能內(nèi)存泄漏的代碼如何去改進呢?
答案就是用棧對象明確的生命期去管理資源。
用對象的生命期管理資源
試想一下,如果我們把之前程序中,對內(nèi)存的分配寫在構(gòu)造函數(shù)中,把釋放資源寫在析構(gòu)函數(shù)中,而棧對象的生命期是明確的,當該管理資源的對象過期時,連同它管理的資源一起釋放,豈不是非常智能化?
我們嘗試著寫出下列代碼:
我們把之前的代碼做如下的改進:
再來分析一下這段代碼:
如果正常執(zhí)行,那么當執(zhí)行完try塊時,scope對象過期,執(zhí)行析構(gòu)函數(shù),同時釋放了那段數(shù)組。如果使用的過程中發(fā)生了異常,那么當程序進入catch塊時,同樣會銷毀try內(nèi)的局部變量。
無論是哪種情況,內(nèi)存總是會被釋放。
如果這里不是int,而是其他復(fù)雜的類型,使用這個封裝的ScopePtr是不是不太方便?顯然不會,我們?nèi)ブ剌d成員操作符就可以了,使它表現(xiàn)的像個指針,這就是一個最簡單的智能指針的產(chǎn)生。
問題得到了完美的解決!
資源獲取即初始化
我們上面解決問題的辦法就是RAII技術(shù),RAII的含義是“資源獲取即初始化”,這個概念有兩個要點:
- 獲得資源后立即放進管理對象
- 管理對象運用析構(gòu)函數(shù)確保資源被釋放
看另外一個例子:我們在訪問一些臨界區(qū)資源的時候通常需要加鎖,所以產(chǎn)生了下面的代碼:
這種方式是很容易出現(xiàn)問題的,例如程序中間遇見錯誤情況需要退出這個函數(shù),此時很容易忘記解鎖:
此時如果再次進行Lock操作,就造成了死鎖。
解決這個問題的辦法仍然很簡單,我們?nèi)懸粋€類:
這樣剛才那段代碼就可以修改成:
這樣,一旦離開這段代碼,程序立刻自動解鎖。
不過為了防止錯誤使用這個類,例如:
可以定義一個宏:
這樣我們在錯誤使用的時候,編譯期間就能發(fā)現(xiàn)錯誤。
一種泛型解決方案
劉未鵬在他的《C++11(及現(xiàn)代C++風格)和快速迭代式開發(fā)》中提出了一種泛型實現(xiàn),利用了C++11的function和Lambda匿名函數(shù),如下:
使用方式也很簡單:
其實就是將該資源釋放的函數(shù)代碼段注冊到Scope類,其中原理不再贅述。
與其他語言的對比
RAII是C++獨有的編程手段。通過RAII技術(shù)我們能夠做到資源不需要使用時立即釋放,這是其他GC語言所不具備的。
以Java為例,Java具有完善的GC(Garbage Collection,垃圾回收)機制,但是存在如下的缺點:
- GC只能回收內(nèi)存,而對于打開的文件、數(shù)據(jù)庫連接等仍然需要手工關(guān)閉。
- GC因為進程優(yōu)先級等原因,回收效率底下,詳情可以參考孟巖的《垃圾收集機制(Garbage Collection)批判》
conclusion
RAII技術(shù)是現(xiàn)代C++編程技術(shù)中及其重要的一部分,甚至有人稱其為“C++編程中最重要的編程技法”,可見其重要性。通過RAII,我們完全可以實現(xiàn)資源的自動化管理,寫出永不內(nèi)存泄漏的程序。
參考資料
- 《C++ Primer》
- 《Effective C++》
- 《Linux多線程服務(wù)器端編程》
總結(jié)
以上是生活随笔為你收集整理的用RAII技术管理资源及其泛型实现的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: mysql grant查看用户权限命令
- 下一篇: 83998 连接服务器出错_服务端 TC