青青草原综合久久大伊人导航_色综合久久天天综合_日日噜噜夜夜狠狠久久丁香五月_热久久这里只有精品

woaidongmao

文章均收錄自他人博客,但不喜標題前加-[轉貼],因其丑陋,見諒!~
隨筆 - 1469, 文章 - 0, 評論 - 661, 引用 - 0
數據加載中……

如何編寫異常安全的C++代碼

 關于C++中異常的爭論何其多也,但往往是一些不合事實的誤解。異常曾經是一個難以用好的語言特性,幸運的是,隨著C++社區經驗的積累,今天我們已經有足夠的知識輕松編寫異常安全的代碼了,而且編寫異常安全的代碼一般也不會對性能造成影響。

  使用異常還是返回錯誤碼?這是個爭論不休的話題。大家一定聽說過這樣的說法:只有在真正異常的時候,才使用異常。那什么是真正異常的時候?在回答這個問題以前,讓我們先看一看程序設計中的不變式原理。

  對象就是屬性聚合加方法,如何判定一個對象的屬性聚合是不是處于邏輯上正確的狀態呢?這可以通過一系列的斷言,最后下一個結論說:這個對象的屬性聚合邏輯上是正確的或者是有問題的。這些斷言就是衡量對象屬性聚合對錯的不變式。

  我們通常在函數調用中,實施不變式的檢查。不變式分為三類:前條件,后條件和不變式。前條件是指在函數調用之前,必須滿足的邏輯條件,后條件是函數調用后必須滿足的邏輯條件,不變式則是整個函數執行中都必須滿足的條件。在我們的討論中,不變式既是前條件又是后條件。前條件是必須滿足的,如果不滿足,那就是程序邏輯錯誤,后條件則不一定。現在,我們可以用不變式來嚴格定義異常狀況了:滿足前條件,但是無法滿足后條件,即為異常狀況。當且僅當發生異常狀況時,才拋出異常。

  關于何時拋出異常的回答中,并不排斥返回值報告錯誤,而且這兩者是正交的。然而,從我們經驗上來說,完全可以在這兩者中加以選擇,這又是為什么呢?事實上,當我們做出這種選擇時,必然意味著接口語意的改變,在不改變接口的情況下,其實是無法選擇的(試試看,用返回值處理構造函數中的錯誤)。通過不變式區別出正常和異常狀況,還可以更好地提煉接口。

  對于異常安全的評定,可分為三個級別:基本保證、強保證和不會失敗。

  基本保證:確保出現異常時程序(對象)處于未知但有效的狀態。所謂有效,即對象的不變式檢查全部通過。
  強保證:確保操作的事務性,要么成功,程序處于目標狀態,要么不發生改變。
  不會失敗:對于大多數函數來說,這是很難保證的。對于C++程序,至少析構函數、釋放函數和swap函數要確保不會失敗,這是編寫異常安全代碼的基礎。

  首先從異常情況下資源管理的問題開始.很多人可能都這么干過:

Type* obj = new Type;
try{  do_something...}
catch(...){ delete obj; throw;}

  不要這么做!這么做只會使你的代碼看上去混亂,而且會降低效率,這也是一直以來異常名聲不大好的原因之一. 請借助于RAII技術來完成這樣的工作:

auto_ptr<Type> obj_ptr(new Type);
do_something...

  這樣的代碼簡潔、安全而且無損于效率。當你不關心或是無法處理異常時,請不要試圖捕獲它。并非使用try...catch才能編寫異常安全的代碼,大部分異常安全的代碼都不需要try...catch。我承認,現實世界并非總是如上述的例子那樣簡單,但是這個例子確實可以代表很多異常安全代碼的做法。在這個例子中,boost::scoped_ptrauto_ptr一個更適合的替代品。

  現在來考慮這樣一個構造函數:

Type() : m_a(new TypeA), m_b(new TypeB){}

  假設成員變量m_am_b是原始的指針類型,并且和Type內的申明順序一致。這樣的代碼是不安全的,它存在資源泄漏問題,構造函數的失敗回滾機制無法應對這樣的問題。如果new TypeB拋出異常,new TypeA返回的資源是得不到釋放機會的.曾經,很多人用這樣的方法避免異常:

Type() : m_a(NULL), m_b(NULL){
     auto_ptr<TypeA> tmp_a(new TypeA);
     auto_ptr<TypeB> tmp_b(new TypeB);
     m_a = tmp_a.release();
     m_b = tmp_b.release();
}

  當然,這樣的方法確實是能夠實現異常安全的代碼的,而且其中實現思想將是非常重要的,在如何實現強保證的異常安全代碼中會采用這種思想.然而這種做法不夠徹底,至少析構函數還是要手動完成的。我們仍然可以借助RAII技術,把這件事做得更為徹底:shared_ptr<TypeA> m_a; shared_ptr<TypeB> m_b;這樣,我們就可以輕而易舉地寫出異常安全的代碼:

Type() : m_a(new TypeA), m_b(new TypeB){}

  如果你覺得shared_ptr的性能不能滿足要求,可以編寫一個接口類似scoped_ptr的智能指針類,在析構函數中釋放資源即可。如果類設計成不可復制的,也可以直接用scoped_ptr。強烈建議不要把auto_ptr作為數據成員使用,scoped_ptr雖然名字不大好,但是至少很安全而且不會導致混亂。

  RAII技術并不僅僅用于上述例子中,所有必須成對出現的操作都可以通過這一技術完成而不必try...catch.下面的代碼也是常見的:

a_lock.lock();
try{ ...} catch(...) {a_lock.unlock();throw;}
a_lock.unlock();

  可以這樣解決,先提供一個成對操作的輔助類:

struct scoped_lock{
    explicit scoped_lock(Lock& lock) : m_l(lock){m_l.lock();}
    ~scoped_lock(){m_l.unlock();}
private: 
    Lock& m_l;
};

  然后,代碼只需這樣寫:

scoped_lock guard(a_lock);
do_something...

  清晰而優雅!繼續考察這個例子,假設我們并不需要成對操作, 顯然,修改scoped_lock構造函數即可解決問題。然而,往往方法名稱和參數也不是那么固定的,怎么辦?可以借助這樣一個輔助類:

template<typename FEnd, typename FBegin>
struct pair_guard{
    pair_guard(FEnd fe, FBegin fb) : m_fe(fe) {if (fb) fb();}
     ~pair_guard(){m_fe();}
private:
     FEnd m_fe;
     ...//
禁止復制
};
typedef pair_guard<function<void () > , function<void()> > simple_pair_guard;

  好了,借助boost,我們可以這樣來編寫代碼了:

simple_pair_guard guard(bind(&Lock::unlock, a_lock), bind(&Lock::lock, a_lock) );
do_something...

  我承認,這樣的代碼不如前面的簡潔和容易理解,但是它更靈活,無論函數名稱是什么,都可以拿來結對。我們可以加強對bind的運用,結合占位符和reference_wrapper,就可以處理函數參數、動態綁定變量。所有我們在catch內外的相同工作,交給pair_guard去完成即可。

  考察前面的幾個例子,也許你已經發現了,所謂異常安全的代碼,竟然就是如何避免try...catch的代碼,這和直覺似乎是違背的。有些時候,事情就是如此違背直覺。異常是無處不在的,當你不需要關心異常或者無法處理異常的時候,就應該避免捕獲異常。除非你打算捕獲所有異常,否則,請務必把未處理的異常再次拋出。try...catch的方式固然能夠寫出異常安全的代碼,但是那樣的代碼無論是清晰性和效率都是難以忍受的,而這正是很多人抨擊C++異常的理由。在C++的世界,就應該按照C++的法則來行事。

  如果按照上述的原則行事,能夠實現基本保證了嗎?誠懇地說,基礎設施有了,但技巧上還不夠,讓我們繼續分析不夠的部分。

  對于一個方法常規的執行過程,我們在方法內部可能需要多次修改對象狀態,在方法執行的中途,對象是可能處于非法狀態的(非法狀態 != 未知狀態),如果此時發生異常,對象將變得無效。利用前述的手段,在pair_guard的析構中修復對象是可行的,但缺乏效率,代碼將變得復雜。最好的辦法是......是避免這么作,這么說有點不厚道,但并非毫無道理。當對象處于非法狀態時,意味著此時此刻對象不能安全重入、不能共享。現實一點的做法是:

  a.每一次修改對象,都確保對象處于合法狀態
  b.或者當對象處于非法狀態時,所有操作決不會失敗。

  在接下來的強保證的討論中細述如何做到這兩點。

  強保證是事務性的,這個事務性和數據庫的事務性有區別,也有共通性。實現強保證的原則做法是:在可能失敗的過程中計算出對象的目標狀態,但是不修改對象,在決不失敗的過程中,把對象替換到目標狀態。考察一個不安全的字符串賦值方法:

string& operator=(const string& rsh){
    if (this != &rsh){
        myalloc locked_pool(m_data);
        locked_pool.deallocate(m_data);
        if (rsh.empty())
        m_data = NULL;
        else{
        m_data = locked_pool.allocate(rsh.size() + 1);
        never_failed_copy(m_data, rsh.m_data, rsh.size() + 1);
        }
    }
    return *this;
}

  locked_pool是為了鎖定內存頁。為了討論的簡單起見,我們假設只有locked_pool構造函數和allocate是可能拋出異常的,那么這段代碼連基本保證也沒有做到。若allocate失敗,則m_data取值將是非法的。參考上面的b條目,我們可以這樣修改代碼:

myalloc locked_pool(m_data);
    locked_pool.deallocate(m_data);   //
進入非法狀態
    m_data = NULL;            //
立刻再次回到合法狀態,且不會失敗
    if(!rsh.empty()){
    m_data = locked_pool.allocate(rsh.size() + 1);
    never_failed_memcopy(m_data, rsh.m_data, rsh.size() + 1);
}

  現在,如果locked_pool失敗,對象不發生改變。如果allocate失敗,對象是一個空字符串,這既不是初始狀態,也不是我們預期的目標狀態,但它是一個合法狀態。我們闡明了實現基本保證所需要的技巧部分,結合前述的基礎設施(RAII的運用),完全可以實現基本保證了...,其實還是有一點疏漏,不過,那就留到最后吧。

  繼續,讓上面的代碼實現強保證:

myalloc locked_pool(m_data);
    char* tmp
NULL;
    if(!rsh.empty()){
    tmp = locked_pool.allocate(rsh.size() + 1);
    never_failed_memcopy(tmp, rsh.m_data, rsh.size() + 1); //
先生成目標狀態
    }
    swap(tmp, m_data);       //
對象安全進入目標狀態
    m_alloc.deallocate(tmp);    //
釋放原有資源

  強保證的代碼多使用了一個局部變量tmp,先計算出目標狀態放在tmp中,然后在安全進入目標狀態,這個過程我們并沒有損失什么東西(代碼清晰性,性能等等)。看上去,實現強保證并不比基本保證困難多少,一般而言,也確實如此。不過,別太自信,舉一種典型的很難實現強保證的例子,對于區間操作的強保證:

for (itr = range.begin(); itr != range.end(); ++itr){
    itr->do_something();
}

  如果某個do_something失敗了,range將處于什么狀態?這段代碼仍然做到了基本保證,但不是強保證的,根據實現強保證的基本原則,我們可以這么做:

tmp = range;
for (itr = tmp.begin(); itr != tmp.end(); ++itr){
    itr->do_something();
}
swap(tmp, range);

  似乎很簡單啊!呵呵,這樣的做法并非不可取,只是有時候行不通。因為我們額外付出了性能的代價,而且,這個代價可能很大。無論如何,我們闡述了實現強保證的方法,怎么取舍則由您決定了。

  接下來討論最后一種異常安全保證:不會失敗。

  通常,我們并不需要這么強的安全保證,但是我們至少必須保證三類過程不會失敗:析構函數,釋放類函數,swap。析構和釋放函數不會失敗,這是RAII技術有效的基石,swap不會失敗,是為了在決不失敗的過程中,把對象替換到目標狀態。我們前面的所有討論都是建立在這三類過程不會失敗的基礎上的,在這里,彌補了上面的那個疏漏。

  一般而言,語言內部類型的賦值、取地址等運算是不會發生異常的,上述三類過程邏輯上也是不會發生異常的。內部運算中,除法運算可能拋出異常。但是地址訪問錯通常是一種錯誤,而不是異常,我們本應該在前條件檢查中就發現的這一點的。所有不會發生異常操作的簡單累加,仍然不會導致異常。

  好了,現在我們可以總結一下編寫異常安全代碼的幾條準則了:

  1.只在應該使用異常的地方拋出異常
  2.如果不知道如何處理異常,請不要捕獲(截留)異常。
  3.充分使用RAII,旁路異常。
  4.努力實現強保證,至少實現基本保證。
  5.確保析構函數、釋放類函數和swap不會失敗。

  另外,還有一些語言細節問題,因為和這個主題有關也一并列出:

  1.不要這樣拋出異常:throw new exception;這將導致內存泄漏。
  2.自定義類型,應該捕獲異常的引用類型:catch(exception& e)catch(const exception& e)
  3.不要使用異常規范,即使是空異常規范。編譯器并不保證只拋出異常規范允許的異常,更多內容請參考相關書籍。

 

posted on 2008-12-26 23:06 肥仔 閱讀(458) 評論(0)  編輯 收藏 引用 所屬分類: C++ 基礎

青青草原综合久久大伊人导航_色综合久久天天综合_日日噜噜夜夜狠狠久久丁香五月_热久久这里只有精品
  • <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>
            亚洲电影免费观看高清| 在线观看不卡| 久久久久综合网| 久久精品天堂| 亚洲国产天堂网精品网站| 亚洲黄一区二区三区| 久久精品国产清高在天天线 | 亚洲黄色在线| 欧美日韩美女在线| 午夜精品在线视频| 久久国内精品视频| 亚洲精品偷拍| 在线视频中文亚洲| 国内精品美女在线观看| 欧美激情在线狂野欧美精品| 欧美日韩精品久久| 欧美日本在线| 国内精品久久久久伊人av| 女人香蕉久久**毛片精品| 欧美精品一区二区三区很污很色的| 亚洲午夜伦理| 久久精品一区| 夜久久久久久| 欧美在线观看www| 亚洲精品乱码久久久久| 亚洲一区bb| 亚洲国产精品一区二区第一页 | 午夜精品久久久| 久久全国免费视频| 亚洲视频1区| 久久久99国产精品免费| 一本在线高清不卡dvd | 久久国产精品99精品国产| 亚洲精品在线观看免费| 亚洲欧美欧美一区二区三区| 亚洲国产精品第一区二区| 亚洲一区二区三区在线观看视频| 影音先锋亚洲视频| 一区二区欧美亚洲| 亚洲大胆人体视频| 亚洲性色视频| 亚洲精品乱码久久久久久久久| 亚洲综合国产激情另类一区| 亚洲人成高清| 亚洲男人影院| 日韩视频中午一区| 在线免费观看日韩欧美| 野花国产精品入口| 亚洲成人在线| 亚洲在线观看视频| 日韩亚洲精品视频| 久久久久久久综合色一本| 亚洲欧美欧美一区二区三区| 欧美α欧美αv大片| 久久狠狠亚洲综合| 欧美色图麻豆| 亚洲福利电影| 狠狠狠色丁香婷婷综合激情| 9i看片成人免费高清| 亚洲国产婷婷综合在线精品| 午夜视频在线观看一区| 一区二区免费在线观看| 久久久一二三| 欧美专区日韩专区| 欧美深夜影院| 亚洲激情欧美| 一区国产精品| 欧美一级片在线播放| 亚洲一区在线播放| 欧美久久久久| 欧美激情精品久久久六区热门 | 亚洲三级国产| 亚洲电影免费观看高清完整版| 久久疯狂做爰流白浆xx| 亚洲欧美国产77777| 一区二区三区高清在线 | 久久综合导航| 国产欧美另类| 中文国产成人精品久久一| 亚洲最新视频在线| 免费在线亚洲欧美| 欧美11—12娇小xxxx| 国内精品久久久久久久影视蜜臀 | 久久久在线视频| 久久久精品国产一区二区三区| 国产精品视频不卡| 中文av一区二区| 一区二区三区视频免费在线观看| 免费成人在线观看视频| 免费成人在线视频网站| 激情久久影院| 久久国产精品一区二区三区四区| 欧美伊人久久久久久久久影院| 国产精品九九久久久久久久| 亚洲免费观看视频| 一区二区三区黄色| 欧美精品乱人伦久久久久久| 亚洲国产欧美一区二区三区久久 | 亚洲欧美另类中文字幕| 欧美亚男人的天堂| 一区二区三区四区国产| 亚洲午夜极品| 国产精品高精视频免费| 亚洲视频精品| 欧美日本国产| 久久久久国产精品厨房| 国产深夜精品| 久久黄色小说| 久久综合伊人77777| 激情五月婷婷综合| 久久美女艺术照精彩视频福利播放| 免费不卡视频| 亚洲欧洲日韩在线| 欧美激情视频一区二区三区不卡| 亚洲国产综合在线看不卡| 亚洲精品久久久久久久久| 欧美激情视频一区二区三区免费| 亚洲精品欧洲| 亚洲免费视频在线观看| 国产精品第十页| 亚洲免费视频在线观看| 亚洲欧美制服另类日韩| 国产日本欧美视频| 久久精品99国产精品| 欧美高潮视频| 99精品国产福利在线观看免费| 欧美日韩免费精品| 一本色道久久综合亚洲精品不| 午夜精品久久久久久99热| 国产日韩欧美精品| 久久婷婷国产综合精品青草 | 国产精品第一区| 亚洲一区二区免费看| 久久九九精品| 亚洲国产美国国产综合一区二区| 美女主播一区| 亚洲精品乱码久久久久久蜜桃麻豆| 中文欧美日韩| 国产日韩一区二区三区在线播放| 久久久久久久综合色一本| 99视频精品免费观看| 午夜精品久久久久久久久| 久久综合国产精品| 亚洲精品一区二区在线观看| 欧美视频免费在线| 欧美一区亚洲一区| 亚洲国产精品毛片| 亚洲欧美在线看| 精品999在线播放| 欧美久久一区| 午夜在线a亚洲v天堂网2018| 免费在线观看精品| 中文欧美在线视频| 激情小说亚洲一区| 欧美高清在线观看| 亚洲综合精品自拍| 欧美岛国在线观看| 亚洲欧洲av一区二区三区久久| 国内视频精品| 欧美日韩国产va另类| 欧美一区二区播放| 最近中文字幕mv在线一区二区三区四区| 亚洲欧美色一区| 亚洲娇小video精品| 国产精品va| 久久一区二区三区国产精品| 一二三区精品福利视频| 麻豆精品精品国产自在97香蕉| 亚洲深夜福利| 在线观看国产精品网站| 国产精品久久久久久久久搜平片| 久久亚洲国产精品日日av夜夜| 一区二区三区你懂的| 玖玖在线精品| 亚洲欧美一区二区精品久久久| 亚洲激情六月丁香| 欧美日韩国产大片| 欧美怡红院视频| 亚洲精品久久在线| 久久亚洲综合色| 久久久久综合| 一本综合久久| 亚洲国产精品福利| 久久激情五月丁香伊人| 亚洲最新在线视频| 在线播放中文一区| 国产精品一区在线播放| 欧美另类女人| 久久久久久久久久久一区| 亚洲一区免费视频| 91久久久久久久久| 久久婷婷麻豆| 欧美一区二区三区免费看| 一本久道久久综合狠狠爱| 亚洲成人资源| 国产日韩欧美日韩| 欧美亚洲成人网| 欧美精品自拍| 嫩草国产精品入口| 久久黄金**|