RAII 與Pimpl 源地址http://tech.uc.cn/?p=851程序員
RAIIsegmentfault
RAII是Bjarne Stroustrup教授用於解決資源分配而發明的技術,資源獲取即初始化。函數
RAII是C++的構造機制的直接使用,即利用構造函數分配資源,利用析構函數來回收資源。佈局
咱們知道,在C/C++語言中,對動態分配的內存的處理必須十分謹慎。在沒有RAII應用的狀況下,若是在內存釋放以前就離開指針的做用域,這時候幾乎沒機會去釋放該內存,除非垃圾回收器對其管制,不然咱們要面對的將會是內存泄漏。性能
舉個例子來講明下RAII在內存分配方面的使用。
this
這是典型的C風格代碼,沒有應用RAII。
所以值得注意的是,destroy_bytearray必須在退出做用域前被調用。
然而在複雜的邏輯設計中,程序員每每要花大量的精力以確認全部在該做用域分配的ByteArray獲得正確的釋放。spa
相形之下,C++運行機制保證了棧上對象一旦即將離開做用域,其析構函數將被執行,給予了釋放資源的時間。注意,在堆分配的對象必須調用delete來結束其生命。
設計
C++11 STL中的std::unique_ptr可用於控制做用域中的動態分配的對象。
譬如:
指針
函數bar()只是增長了一行,但強壯了不少,函數bar()執行完或者有異常拋出時,holder總會被析構,從而ba或被delete。code
下面是ByteArray的Ada實現:
-- lib.ads withinterfaces; withAda.Finalization; packagelib is typeuchars isarray(positive range<>)ofinterfaces.unsigned_8; typeuchars_p isaccessuchars; typeByteArray isnewAda.Finalization.Limited_Controlled withprivate; functionCreate(length:integer)returnByteArray; private typeByteArray isnewAda.Finalization.Limited_Controlled withrecord length:integer; data:uchars_p; endrecord; overriding procedureInitialize(This:inoutByteArray); overriding procedureFinalize(This:inoutByteArray); endlib; -- lib.adb withAda.Unchecked_Deallocation; packagebodylib is useAda.Finalization; functionCreate(length:integer)returnByteArray is begin iflength<0then put_line("Create"); returnByteArray'(Limited_Controlled with length => length, data=> new uchars(1..length)); end if; return ByteArray'(Limited_Controlled withlength=>0,data=>null); endCreate; overriding procedureInitialize(This:inoutByteArray)is begin put_line("Initialize"); this.length:=0; this.data:=null; endInitialize; overriding procedureFinalize(This:inoutByteArray)is procedurefree isnewAda.Unchecked_Deallocation(uchars,uchars_p); begin put_line("Finalize"); if(this.data/=null)then free(this.data); endif; endFinalize; endlib; -- main.adb withlib; uselib; proceduremain is K:ByteArray:=Create(10240); C:ByteArray; begin null; endmain;
– 輸出以下
./main
Create
Initialize
Finalize
Finalize
另外一種狀況是對I/O資源的處理,當咱們再也不使用資源時,必須將資源歸還給系統。
下面例子來自 wikipedia的RAII條目:
在write_to_file函數中,RAII做用於std::ofstream和std::lock_guard,從而保證了函數write_to_file在返回時,lock和file總會調用自身的析構函數,對於lock而言,它會釋放mutex,而file則會close。
Pimpl
Pimpl(pointer to implementation),是一種應用十分普遍的技術,它的別名也不少,如Opaque pointer, handle classes等。
wikipedia上已經對其就Ada、C和C++舉例,這裏不做舉例。
我的認爲,Pimpl是RAII的延展,籍由RAII對資源的控制,把具體的數據佈局和實現從調用者視線內移開,從而簡化了API接口,也使得ABI兼容變得有可能,Qt和KDE正是使用Pimpl來維護API的一致性,另外也爲惰性初始化提供途徑,以及隱式共享提供了基礎。
我在設計代碼時也會考慮使用Pimpl,但不是必然使用,由於Pimpl也會帶來反作用,主要有兩方面
Pimpl指針致使內存空間開銷增大
類型間Pimpl的訪問須要較多間接的指針跳轉,甚至還用使用friend''來提高訪問權限,如如下代碼中,Teacher能夠訪問Student的Context。
儘管如此,我我的仍是在面向開發應用的接口中會盡可能使用Pimpl來維護API和ABI的一致性,除非Pimpl會引發顯著的性能降低。