• <ins id="pjuwb"></ins>
    <blockquote id="pjuwb"><pre id="pjuwb"></pre></blockquote>
    <noscript id="pjuwb"></noscript>
          <sup id="pjuwb"><pre id="pjuwb"></pre></sup>
            <dd id="pjuwb"></dd>
            <abbr id="pjuwb"></abbr>

            alex

            alex

              C++博客 :: 首頁 :: 新隨筆 :: 聯系 :: 聚合  :: 管理 ::
              7 隨筆 :: 6 文章 :: 7 評論 :: 0 Trackbacks

            今天來講講怎么編寫異常安全的代碼。
            程序運行過程中,往往需要對一些流程加入異常處理,來提高程序的robust.比如
            通過try catch來捕捉異常
            try
            {
            ??? pMemory = new char[MAX_BUF_SIZE];
            }
            catch(std::bad_alloc& e)
            {
            ??? //error handling,er:do something resource free
            }
            但在程序代碼段中出現大量的try catch,不僅從美觀,效率和程序輸寫上都是不怎么好。
            而另外一種對于異常的處理方法是依賴于c++的ctor/dctor的匹配來做的,就是所謂的
            RAII,這個很容易讓人聯想到std::auto_ptr
            std::auto_ptr<int> tmp(new int);
            通過new分配的對象會在tmp生命結束后,釋放相關的資源,通過這種方式,就能保證在程序異常,或退出時,已分配的對象能正確自動的釋放擁有的資源,而在對象聲明周期內,可以保證資源的有效性。
            這種方式就是今天blog要寫的主要內容,我們可以看到std::auto_ptr作用范圍很小,只能對從堆上分配的對象進行管理,假如對文件打開句柄實行RAII,你也許會認為再寫個不就是了,但這樣只會造成源代碼里充滿了這些資源管理的類,這導致了一個嚴重的問題,好的結構在繁瑣的流暢前面變的難堪。
            那怎么樣對這個進行泛化,從而能對比如從簡單的指針釋放,文件句柄維護,甚至相關的成員函數。我們來看下loki::socpeguard是怎么實現的:
            先看下基本的用法
            (1)? FILE* hFileOpen = fopen(....);
            (2)? LOKI_ON_BLOCK_EXIT(flose,hFileOpen);
            line2會在出LOKI_ON_BLOCK_EXIT域或程序異常結束時被調用,下面是對類成員的調用
            void CTestObject::Create
            {
            ??? LOKI_ON_BLOCK_EXIT_OBJ(*this,FressResouce);
            ??? ...
            }
            同上面差不多,會在這個函數結束后或異常結束后調用類成員的FreeResource.在正常流程結束后,可以通過調用Dismiss來防止對FreeResouce的調用,即類似數據庫操作的commit操作。下面來分析下LOKI的實現:
            從上面可以看到,RAII的是:
            1:構造函數對資源的獲取
            2:稀構函數對資源的釋放
            先來看下LoKi對上面那2個宏的定義
            #define LOKI_ON_BLOCK_EXIT????? Loki::ScopeGuard LOKI_ANONYMOUS_VARIABLE(scopeGuard) = Loki::MakeGuard

            #define LOKI_ON_BLOCK_EXIT_OBJ? Loki::ScopeGuard LOKI_ANONYMOUS_VARIABLE(scopeGuard) = Loki::MakeObjGuard
            上面的Loki::ScopeGuard是一個基類的別名
            typedef const ScopeGuardImplBase& ScopeGuard;
            而LOKI_ANONYMOUS_VARIABLE(scopeGuard)用來我們產生唯一的名字,有時假如需要調用Dismiss的話,則需要自己去實現宏定義的內容,這樣才能通過對象訪問。Loki::MakeGuard或Loki::MakeObjGuard是用來產生對象的實際類型的,下面是一個
            Loki::MakeGuard的例子:
            template <typename F, typename P1>
            inline ScopeGuardImpl1<F, P1> MakeGuard(F fun, P1 p1)
            {
            ??? return ScopeGuardImpl1<F, P1>::MakeGuard(fun, p1);
            }
            可以看到ScopeGuardImpl1<F, P1>是要產生的具體類型,MakeGuard通過函數參數的數目來重載的,而MakeGuard此處的作用是要睡死了...-_-'',作用是利用函數自動推導出參數的類型,這樣就免去了指定ScopeGuardImpl1的類型的麻煩,而
            ScopeGuardImpl1<F, P1>::MakeGuard(fun, p1);
            簡單的返回對象的一個臨時變量,并assign給一個上面的一個scopeguard的實例,這里依賴一個C++的特性,臨時變量的聲命周期和通過他初始化的引用類型的聲明周期是一致的。

            從上面可以看到Loki定義了一個ScopeGuardImplBase的基礎類。這個類定義了一個基本的方法Dismiss,以及相關的狀態。下面是loki中這個類的定義
            class ScopeGuardImplBase
            {
            ??? ScopeGuardImplBase& operator =(const ScopeGuardImplBase&);
            ?protected:
            ??? ~ScopeGuardImplBase()
            ??? {}
            ??? ScopeGuardImplBase(const ScopeGuardImplBase& other) throw()
            ??????? : dismissed_(other.dismissed_)
            ??? {
            ??????? other.Dismiss();
            ??? }
            ??? template <typename J>
            ??? static void SafeExecute(J& j) throw()
            ??? {
            ??????? if (!j.dismissed_)
            ?????????? try
            ?????????? {
            ????????????? j.Execute();
            ?????????? }
            ?????????? catch(...)
            ?????????? {}
            ???? }
            ???????
            ???? mutable bool dismissed_;
            public:
            ???? ScopeGuardImplBase() throw() : dismissed_(false)
            ???? {}
            ???? void Dismiss() const throw()
            ???? {
            ???????? dismissed_ = true;
            ????? }
            };
            可以看到類里面定義了上面所說的一些屬性,其中SafeExecute用來提供子類同一的資源釋放方法,并調用子類的方法來具體操作,因為相關的函數,變量都保存在具體的子類,可以看到這個函數使用了try catch,這里加這個的目的是,因為資源釋放要在子類的稀構里被觸發,而調用具體的方法是外面傳進來的,所以無法保證一定是異常安全的,而假如在稀構里面異常的話,會導致程序的行為無法定義。
            下面具體來看下一個子類的實現:
            template <typename F, typename P1>
            class ScopeGuardImpl1 : public ScopeGuardImplBase
            {
            public:
            ??? static ScopeGuardImpl1<F, P1> MakeGuard(F fun, P1 p1)
            ??? {
            ??????? return ScopeGuardImpl1<F, P1>(fun, p1);
            ??? }
            ??? ~ScopeGuardImpl1() throw()
            ??? {
            ??????? SafeExecute(*this);
            ??? }
            ??? void Execute()
            ??? {
            ??????? fun_(p1_);
            ??? }
            protected:
            ??? ScopeGuardImpl1(F fun, P1 p1) : fun_(fun), p1_(p1)
            ??? {}
            ??? F fun_;
            ??? const P1 p1_;
            };
            在LoKi里面可以看到很多類似ScopeGuardImpl1的定義,比如ScopeGuardImpl0,
            ScopeGuardImpl2,可以發現最后面的數字表示具體參數的數目。
            可以看到上面所說的MakeGuard的定義,以及對基類方法的調用,可以看到構造函數接收的類型,一個函數對象,和一些參數對象,并保存,對于成員函數的scopeguard,LoKi定義了1些相似的類,主要是增加了對象的引用,還有就是函數的調用方式上。
            上面可以看到參數是通過值的方式來保存的而不是通過引用。而且是const屬性的,下面是相關的分析。
            1:通過傳值的方式,從而避免了異常拋出時,可能引用的對象被稀構
            2:加const屬性,從而保證了在func需要參數是reference時而保存的參數確是非const時產生相應的編譯錯誤,因為對reference傳人const non-reference形式是錯誤的。
            而對于1的方式,存在的一種問題是假如操作的fun需要傳入引用,那傳進去的值就無法在釋放的函數中被改變,而2是對這種的一種類似契約似的編程,Loki 提供的方法是通過一個中間對象來保存操作參數的引用,并賦予這個對象自動轉換功能。下面是這個類的定義:
            template <class T>
            class RefToValue
            {??
            public:
            ??? RefToValue(T& ref) : ref_(ref)
            ??? {}
            ??? RefToValue(const RefToValue& rhs) : ref_(rhs.ref_)
            ??? {}
            ??? operator T& () const
            ??? {
            ??????? return ref_;
            ??? }
            private:
            ??? // Disable - not implemented
            ??? RefToValue();
            ??? RefToValue& operator=(const RefToValue&);
            ???????
            ??? T& ref_;
            };
            可以很清楚的看到類的實現,下面是一個工具類
            template <class T>
            inline RefToValue<T> ByRef(T& t)
            {
            ???? return RefToValue<T>(t);
            }
            下面給個具體的例子,假如
            template<typename _Ty>
            void SAFEDELETE(_Ty*& ptr)
            {
            ?? if (NULL != ptr)
            ????? delete ptr;
            ?? ptr = NULL;
            }

            char* ptr = new char;
            ?
            {
            ??? LOKI_ON_BLOCK_EXIT(SAFEDELETE<char>,Loki::ByRef(ptr));
            }
            ?
            if (NULL == ptr)
            ? std::cout << "NULL" << std::endl;
            基本上就這么多了,sleep去了
            ??????????????????????????????????????????????????? alex_yuu

            posted on 2007-02-11 15:55 agerlis 閱讀(1379) 評論(3)  編輯 收藏 引用

            評論

            # re: 編寫異常安全的代碼(loki::scopeguard) 2007-02-11 15:56 agerlis
            請問,怎么設置背景色?  回復  更多評論
              

            # re: 編寫異常安全的代碼(loki::scopeguard) 2007-02-12 16:13 mc
            不錯,把Loki::ScopeGuard介紹的比較透徹了:)  回復  更多評論
              

            # re: 編寫異常安全的代碼(loki::scopeguard) 2007-05-28 16:40 candy
            看到這些代碼我就暈了,厲害啊,我比較討厭編程。。嘿嘿,我是龔芳萍。  回復  更多評論
              

            亚洲女久久久噜噜噜熟女| 品成人欧美大片久久国产欧美...| 久久国产视频网| 久久婷婷是五月综合色狠狠| 亚洲欧美伊人久久综合一区二区| 久久久精品2019免费观看| 国产精品xxxx国产喷水亚洲国产精品无码久久一区 | 少妇内射兰兰久久| 青草影院天堂男人久久| 模特私拍国产精品久久| www.久久热| 久久亚洲国产成人精品性色| 国产精品久久久99| 蜜臀久久99精品久久久久久小说 | 久久人人爽人人爽AV片| 伊人久久精品无码二区麻豆| 国产免费久久精品丫丫| 久久久久亚洲av无码专区| 亚洲&#228;v永久无码精品天堂久久 | 国产产无码乱码精品久久鸭| 久久99精品国产99久久6| 精品无码久久久久久午夜| 伊人色综合九久久天天蜜桃| 久久久久久久尹人综合网亚洲| 久久国产欧美日韩精品免费| 91精品国产91热久久久久福利| 国产偷久久久精品专区 | 国产精品VIDEOSSEX久久发布| 久久精品国产亚洲AV大全| 久久国产免费直播| 久久精品国产亚洲AV久| 亚洲国产成人乱码精品女人久久久不卡| 久久国产精品99精品国产987| 久久久久久人妻无码| 色婷婷综合久久久久中文| 久久亚洲AV无码精品色午夜麻豆| 久久中文精品无码中文字幕| 久久99精品免费一区二区| 久久国产免费| 欧美性猛交xxxx免费看久久久| 久久精品无码免费不卡|