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

woaidongmao

文章均收錄自他人博客,但不喜標(biāo)題前加-[轉(zhuǎn)貼],因其丑陋,見諒!~
隨筆 - 1469, 文章 - 0, 評(píng)論 - 661, 引用 - 0
數(shù)據(jù)加載中……

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

 關(guān)于C++中異常的爭(zhēng)論何其多也,但往往是一些不合事實(shí)的誤解。異常曾經(jīng)是一個(gè)難以用好的語言特性,幸運(yùn)的是,隨著C++社區(qū)經(jīng)驗(yàn)的積累,今天我們已經(jīng)有足夠的知識(shí)輕松編寫異常安全的代碼了,而且編寫異常安全的代碼一般也不會(huì)對(duì)性能造成影響。

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

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

  我們通常在函數(shù)調(diào)用中,實(shí)施不變式的檢查。不變式分為三類:前條件,后條件和不變式。前條件是指在函數(shù)調(diào)用之前,必須滿足的邏輯條件,后條件是函數(shù)調(diào)用后必須滿足的邏輯條件,不變式則是整個(gè)函數(shù)執(zhí)行中都必須滿足的條件。在我們的討論中,不變式既是前條件又是后條件。前條件是必須滿足的,如果不滿足,那就是程序邏輯錯(cuò)誤,后條件則不一定。現(xiàn)在,我們可以用不變式來嚴(yán)格定義異常狀況了:滿足前條件,但是無法滿足后條件,即為異常狀況。當(dāng)且僅當(dāng)發(fā)生異常狀況時(shí),才拋出異常。

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

  對(duì)于異常安全的評(píng)定,可分為三個(gè)級(jí)別:基本保證、強(qiáng)保證和不會(huì)失敗。

  基本保證:確保出現(xiàn)異常時(shí)程序(對(duì)象)處于未知但有效的狀態(tài)。所謂有效,即對(duì)象的不變式檢查全部通過。
  強(qiáng)保證:確保操作的事務(wù)性,要么成功,程序處于目標(biāo)狀態(tài),要么不發(fā)生改變。
  不會(huì)失敗:對(duì)于大多數(shù)函數(shù)來說,這是很難保證的。對(duì)于C++程序,至少析構(gòu)函數(shù)、釋放函數(shù)和swap函數(shù)要確保不會(huì)失敗,這是編寫異常安全代碼的基礎(chǔ)。

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

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

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

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

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

  現(xiàn)在來考慮這樣一個(gè)構(gòu)造函數(shù):

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

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

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();
}

  當(dāng)然,這樣的方法確實(shí)是能夠?qū)崿F(xiàn)異常安全的代碼的,而且其中實(shí)現(xiàn)思想將是非常重要的,在如何實(shí)現(xiàn)強(qiáng)保證的異常安全代碼中會(huì)采用這種思想.然而這種做法不夠徹底,至少析構(gòu)函數(shù)還是要手動(dòng)完成的。我們?nèi)匀豢梢越柚?span lang="EN-US">RAII技術(shù),把這件事做得更為徹底:shared_ptr<TypeA> m_a; shared_ptr<TypeB> m_b;這樣,我們就可以輕而易舉地寫出異常安全的代碼:

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

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

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

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

  可以這樣解決,先提供一個(gè)成對(duì)操作的輔助類:

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...

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

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;
     ...//
禁止復(fù)制
};
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...

  我承認(rèn),這樣的代碼不如前面的簡(jiǎn)潔和容易理解,但是它更靈活,無論函數(shù)名稱是什么,都可以拿來結(jié)對(duì)。我們可以加強(qiáng)對(duì)bind的運(yùn)用,結(jié)合占位符和reference_wrapper,就可以處理函數(shù)參數(shù)、動(dòng)態(tài)綁定變量。所有我們?cè)?span lang="EN-US">catch內(nèi)外的相同工作,交給pair_guard去完成即可。

  考察前面的幾個(gè)例子,也許你已經(jīng)發(fā)現(xiàn)了,所謂異常安全的代碼,竟然就是如何避免try...catch的代碼,這和直覺似乎是違背的。有些時(shí)候,事情就是如此違背直覺。異常是無處不在的,當(dāng)你不需要關(guān)心異常或者無法處理異常的時(shí)候,就應(yīng)該避免捕獲異常。除非你打算捕獲所有異常,否則,請(qǐng)務(wù)必把未處理的異常再次拋出。try...catch的方式固然能夠?qū)懗霎惓0踩拇a,但是那樣的代碼無論是清晰性和效率都是難以忍受的,而這正是很多人抨擊C++異常的理由。在C++的世界,就應(yīng)該按照C++的法則來行事。

  如果按照上述的原則行事,能夠?qū)崿F(xiàn)基本保證了嗎?誠(chéng)懇地說,基礎(chǔ)設(shè)施有了,但技巧上還不夠,讓我們繼續(xù)分析不夠的部分。

  對(duì)于一個(gè)方法常規(guī)的執(zhí)行過程,我們?cè)诜椒▋?nèi)部可能需要多次修改對(duì)象狀態(tài),在方法執(zhí)行的中途,對(duì)象是可能處于非法狀態(tài)的(非法狀態(tài) != 未知狀態(tài)),如果此時(shí)發(fā)生異常,對(duì)象將變得無效。利用前述的手段,在pair_guard的析構(gòu)中修復(fù)對(duì)象是可行的,但缺乏效率,代碼將變得復(fù)雜。最好的辦法是......是避免這么作,這么說有點(diǎn)不厚道,但并非毫無道理。當(dāng)對(duì)象處于非法狀態(tài)時(shí),意味著此時(shí)此刻對(duì)象不能安全重入、不能共享。現(xiàn)實(shí)一點(diǎn)的做法是:

  a.每一次修改對(duì)象,都確保對(duì)象處于合法狀態(tài)
  b.或者當(dāng)對(duì)象處于非法狀態(tài)時(shí),所有操作決不會(huì)失敗。

  在接下來的強(qiáng)保證的討論中細(xì)述如何做到這兩點(diǎn)。

  強(qiáng)保證是事務(wù)性的,這個(gè)事務(wù)性和數(shù)據(jù)庫的事務(wù)性有區(qū)別,也有共通性。實(shí)現(xiàn)強(qiáng)保證的原則做法是:在可能失敗的過程中計(jì)算出對(duì)象的目標(biāo)狀態(tài),但是不修改對(duì)象,在決不失敗的過程中,把對(duì)象替換到目標(biāo)狀態(tài)。考察一個(gè)不安全的字符串賦值方法:

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是為了鎖定內(nèi)存頁。為了討論的簡(jiǎn)單起見,我們假設(shè)只有locked_pool構(gòu)造函數(shù)和allocate是可能拋出異常的,那么這段代碼連基本保證也沒有做到。若allocate失敗,則m_data取值將是非法的。參考上面的b條目,我們可以這樣修改代碼:

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

  現(xiàn)在,如果locked_pool失敗,對(duì)象不發(fā)生改變。如果allocate失敗,對(duì)象是一個(gè)空字符串,這既不是初始狀態(tài),也不是我們預(yù)期的目標(biāo)狀態(tài),但它是一個(gè)合法狀態(tài)。我們闡明了實(shí)現(xiàn)基本保證所需要的技巧部分,結(jié)合前述的基礎(chǔ)設(shè)施(RAII的運(yùn)用),完全可以實(shí)現(xiàn)基本保證了...,其實(shí)還是有一點(diǎn)疏漏,不過,那就留到最后吧。

  繼續(xù),讓上面的代碼實(shí)現(xiàn)強(qiáng)保證:

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); //
先生成目標(biāo)狀態(tài)
    }
    swap(tmp, m_data);       //
對(duì)象安全進(jìn)入目標(biāo)狀態(tài)
    m_alloc.deallocate(tmp);    //
釋放原有資源

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

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

  如果某個(gè)do_something失敗了,range將處于什么狀態(tài)?這段代碼仍然做到了基本保證,但不是強(qiáng)保證的,根據(jù)實(shí)現(xiàn)強(qiáng)保證的基本原則,我們可以這么做:

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

  似乎很簡(jiǎn)單啊!呵呵,這樣的做法并非不可取,只是有時(shí)候行不通。因?yàn)槲覀冾~外付出了性能的代價(jià),而且,這個(gè)代價(jià)可能很大。無論如何,我們闡述了實(shí)現(xiàn)強(qiáng)保證的方法,怎么取舍則由您決定了。

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

  通常,我們并不需要這么強(qiáng)的安全保證,但是我們至少必須保證三類過程不會(huì)失敗:析構(gòu)函數(shù),釋放類函數(shù),swap。析構(gòu)和釋放函數(shù)不會(huì)失敗,這是RAII技術(shù)有效的基石,swap不會(huì)失敗,是為了在決不失敗的過程中,把對(duì)象替換到目標(biāo)狀態(tài)。我們前面的所有討論都是建立在這三類過程不會(huì)失敗的基礎(chǔ)上的,在這里,彌補(bǔ)了上面的那個(gè)疏漏。

  一般而言,語言內(nèi)部類型的賦值、取地址等運(yùn)算是不會(huì)發(fā)生異常的,上述三類過程邏輯上也是不會(huì)發(fā)生異常的。內(nèi)部運(yùn)算中,除法運(yùn)算可能拋出異常。但是地址訪問錯(cuò)通常是一種錯(cuò)誤,而不是異常,我們本應(yīng)該在前條件檢查中就發(fā)現(xiàn)的這一點(diǎn)的。所有不會(huì)發(fā)生異常操作的簡(jiǎn)單累加,仍然不會(huì)導(dǎo)致異常。

  好了,現(xiàn)在我們可以總結(jié)一下編寫異常安全代碼的幾條準(zhǔn)則了:

  1.只在應(yīng)該使用異常的地方拋出異常
  2.如果不知道如何處理異常,請(qǐng)不要捕獲(截留)異常。
  3.充分使用RAII,旁路異常。
  4.努力實(shí)現(xiàn)強(qiáng)保證,至少實(shí)現(xiàn)基本保證。
  5.確保析構(gòu)函數(shù)、釋放類函數(shù)和swap不會(huì)失敗。

  另外,還有一些語言細(xì)節(jié)問題,因?yàn)楹瓦@個(gè)主題有關(guān)也一并列出:

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

 

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

青青草原综合久久大伊人导航_色综合久久天天综合_日日噜噜夜夜狠狠久久丁香五月_热久久这里只有精品
  • <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>
            美女精品在线| 免费日韩精品中文字幕视频在线| 国产欧美日韩免费| 一区二区欧美亚洲| 欧美日韩国产成人| 欧美承认网站| 欧美黄色网络| 国产精品一区毛片| 欧美在线视频全部完| 欧美影院成人| 欧美金8天国| 亚洲男人天堂2024| 欧美成人免费视频| 亚洲电影在线看| 亚洲最新在线视频| 欧美中日韩免费视频| 欧美电影专区| 国产精品一区一区| 亚洲国产女人aaa毛片在线| 欧美激情精品久久久久久大尺度 | 亚洲视频一二| 国产一区二区| 亚洲美女在线视频| 国产综合激情| 日韩一级成人av| 久久偷看各类wc女厕嘘嘘偷窃| 亚洲午夜一区二区三区| 伊人春色精品| 亚洲视频网在线直播| 男女精品视频| 免费影视亚洲| 一区二区三区四区五区视频| 亚洲黄色片网站| 午夜日韩电影| 国产精品视频xxx| 亚洲小说春色综合另类电影| 中文在线资源观看视频网站免费不卡| 欧美一区二区三区电影在线观看| 亚洲欧洲精品一区二区三区不卡 | 欧美一区永久视频免费观看| 欧美日韩爆操| 亚洲美女一区| 亚洲人成小说网站色在线| 国产亚洲一级| 国产一区二区三区无遮挡| 国产欧美一区二区三区另类精品 | 在线亚洲自拍| 欧美在线视频a| 久久久久久**毛片大全| 国产免费一区二区三区香蕉精| 玖玖综合伊人| 亚洲中字在线| 尤物在线精品| 欧美视频日韩视频| 久久久久久久综合| 久久久夜夜夜| 亚洲免费中文字幕| 欧美日韩亚洲一区二| 亚洲乱码国产乱码精品精天堂 | 国产精品日韩在线| 国产乱码精品一区二区三| 一二三区精品福利视频| 亚洲一区观看| 亚洲精选在线| 国产精品红桃| 欧美中文在线免费| 久久精品1区| 亚洲国产成人久久综合一区| 99精品欧美一区二区蜜桃免费| 欧美肥婆bbw| 一区二区三区四区五区视频| 欧美日韩一区二区三区在线视频| 亚洲在线观看视频网站| 午夜精品剧场| 1024亚洲| 日韩视频在线观看国产| 久久精品综合网| 亚洲国产一区二区三区在线播| 欧美风情在线观看| 亚洲欧美福利一区二区| 免费黄网站欧美| 日韩图片一区| 美女网站在线免费欧美精品| 亚洲欧洲三级| 国产精品va在线播放| 久久久久久久久久码影片| 亚洲精品中文字幕在线| 国产精品白丝av嫩草影院| 最新国产乱人伦偷精品免费网站 | 狠狠色狠狠色综合日日91app| 日韩午夜av| 欧美主播一区二区三区| 国产视频欧美| 国产精品99久久久久久www| 欧美一区二区三区视频免费播放| 亚洲欧洲三级电影| 亚洲欧洲久久| 小辣椒精品导航| 黄色成人免费网站| 亚洲免费在线观看| 欧美一区二区视频在线观看| 亚洲激情视频在线| 午夜精品视频网站| 国产精品久久久久久av下载红粉 | 国产尤物精品| 亚洲一区国产精品| 久久精品成人一区二区三区| 亚洲视频欧洲视频| 欧美顶级艳妇交换群宴| 亚洲国产精品成人精品| 亚洲一区二区三区免费视频 | 久久这里只有| 国产欧美欧洲在线观看| 99视频精品全部免费在线| 欧美日韩免费观看一区=区三区| 在线播放不卡| 午夜精品久久久99热福利| 亚洲一区二区动漫| 欧美经典一区二区| 欧美国产精品人人做人人爱| 免费在线国产精品| 亚洲国产精品一区制服丝袜| 一区二区三区日韩精品| 国产精品久久久久久久久免费樱桃| 一本一本久久| 99综合在线| 亚洲精选国产| 免费亚洲电影| 欧美国产精品劲爆| 欧美精品久久久久久久免费观看| 久久九九久久九九| 久久综合免费视频影院| 久久久久久夜| 国内久久视频| 亚洲人成在线观看网站高清| 欧美啪啪一区| 91久久在线观看| 国产精品久久久爽爽爽麻豆色哟哟| 亚洲欧洲日产国产综合网| 亚洲毛片在线| 欧美日韩亚洲91| 久久三级福利| 激情成人亚洲| 欧美va亚洲va香蕉在线| 亚洲精品国产精品国自产观看浪潮| 日韩视频精品在线观看| 亚洲亚洲精品三区日韩精品在线视频| 久久精品国产亚洲一区二区三区| 久久精品综合一区| 亚洲精品你懂的| 亚洲视频播放| 亚洲丰满少妇videoshd| 一区二区不卡在线视频 午夜欧美不卡在 | 最新日韩av| 日韩午夜在线观看视频| 国产精品一二| 免费看成人av| 国产精品美女诱惑| 欧美一区二区观看视频| 免费不卡中文字幕视频| 日韩亚洲国产欧美| 国产精品爽黄69| 久久精品视频在线看| 欧美激情第1页| 亚洲欧美激情视频在线观看一区二区三区 | 亚洲国产日韩精品| 亚洲欧美日韩电影| 久久国产综合精品| 亚洲人成在线观看一区二区| 国产精品国产一区二区 | 欧美激情免费在线| 亚洲综合电影| 亚洲欧洲日夜超级视频| 久久国产精品一区二区三区四区| 欧美日韩国产精品专区| 亚洲免费婷婷| 亚洲国产精品一区二区www在线| 亚洲欧美在线观看| 国产精品第三页| 久久久亚洲国产美女国产盗摄| 亚洲精品乱码久久久久久蜜桃91| 1024日韩| 美女999久久久精品视频| 亚洲午夜小视频| 亚洲国产精品一区二区第四页av | 夜夜嗨一区二区| 尤物yw午夜国产精品视频| 国产精品久久久久av| 中文一区在线| 欧美成人日本| 亚洲精品乱码久久久久久蜜桃91 | 日韩午夜在线观看视频| 蜜桃伊人久久| 久久久久久久综合狠狠综合| 亚洲深夜av| 欧美色图天堂网| 嫩草成人www欧美| 欧美激情免费在线| 麻豆精品一区二区av白丝在线| 亚洲视频在线免费观看|