轉 PIMPL

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會引發顯著的性能降低。

相關文章
相關標籤/搜索