日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 编程语言 > c/c++ >内容正文

c/c++

[Effective C++ --029]为“异常安全”而努力是值得的

發(fā)布時間:2024/4/17 c/c++ 28 豆豆
生活随笔 收集整理的這篇文章主要介紹了 [Effective C++ --029]为“异常安全”而努力是值得的 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

假設(shè)有個class用來表現(xiàn)夾帶背景圖案的GUI菜單單,這個class用于多線程環(huán)境,所以它有個互斥器(mutex)作為并發(fā)控制用:

1 class PrettyMenu{ 2 public: 3 ... 4 void changeBackground(std::istream& imgSrc); 5 ... 6 private: 7 Mutex mutex; 8 Image* bgImage; 9 int imageChanges; 10 }; 11 void PrettyMenu::changeBackground(std::istream& imgSrc) 12 { 13 lock(&mutex); 14 delete bgImage; 15 ++imageChanges; 16 bgImage = new Image(imgSrc); 17 unlock(&mutex); 18 }

從異常安全性的角度看,這個函數(shù)很糟。因為沒有滿足異常安全的兩個條件:

1.不泄露任何資源。上述代碼沒有做到這一點,因為一旦“new Image(imgSrc)”導(dǎo)致異常,對unlock就不會執(zhí)行,于是互斥器就永遠(yuǎn)被把持住了。

2.不允許數(shù)據(jù)破壞。如果“new Image(imgSrc)”拋出異常,bgImage就指向一個已被刪除的對象,imageChanges也已被累加,而其實并沒有新的圖像被成功安裝起來。

?

解決資源泄漏的問題很容易,因為條款13已經(jīng)教會我們“以對象去管理資源”,而條款14也逃入了Lock class作為一種“確保互斥器被及時釋放”的方法:

1 void PrettyMenu::changeBackground(std::istream& imgSrc) 2 { 3 Lock ml(&mutex); //來自條款14; 4 delete bgImage; 5 ++imageChanges; 6 bgImage = new Image(imgSrc); 7 }

關(guān)于“資源管理類”如Lock,一個最棒的事情是,它們通常使函數(shù)更短。較少的代碼就是較好的代碼,因為出錯的機(jī)會比較少。

?

異常安全函數(shù)(Exception-safe function)提供以下三個保證之一:

1.基本承諾:如果異常被拋出,程序內(nèi)的任何事物仍然保持在有效狀態(tài)下。沒有任何對象或數(shù)據(jù)結(jié)構(gòu)會因此而敗壞,所有對象都處于一種內(nèi)部前后一致的狀態(tài)(例如所有的class約束條件都繼續(xù)獲得滿足)。然而程序的現(xiàn)實狀態(tài)恐怕不可預(yù)料。如上例changeBackground使得一旦有異常被拋出時,PrettyMenu對象可以繼續(xù)擁有原背景圖像,或是令它擁有某個缺省背景圖像,但客戶無法預(yù)期哪一種情況。如果想知道,它們恐怕必須調(diào)用某個成員函數(shù)以得知當(dāng)時的背景圖像是什么。

2.強烈保證:如果異常被拋出, 程序狀態(tài)不改變。如果函數(shù)成功,就是完全成功,否則,程序會回復(fù)到“調(diào)用函數(shù)之前”的狀態(tài)。

3.不拋擲(nothrow)保證:承諾絕不拋出異常,因為它們總是能夠完成它們原先承諾的功能。作用于內(nèi)置類型(如ints,指針等等)上的所有操作都提供nothrow保證。帶著“空白異常明細(xì)”的函數(shù)必為nothrow函數(shù),其實不盡然

1 int doSomething() throw(); //”空白異常明細(xì)”

這并不是說doSomething絕不會拋出異常,而是說如果拋出異常,將是嚴(yán)重錯誤,會有你意想不到的函數(shù)被調(diào)用。實際上doSomething也許完全沒有提供任何異常保證。函數(shù)的聲明式(包括異常明細(xì))并不能告訴你是否它是正確的、可移植的或高效的,也不能告訴你它是否提供任何異常安全性保證。

?

一般而言,應(yīng)該會想提供可實施的最強烈保證。nothrow函數(shù)很棒,但我們很難再c part of c++領(lǐng)域中完全沒有調(diào)用任何一個可能拋出異常的函數(shù)。所以大部分函數(shù)而言,抉擇往往落在基本保證和強烈保證之間。

對changeBackground而言,首先,從一個類型為Image*的內(nèi)置指針改為一個“用于資源管理”的智能指針,第二,重新排列changeBackground內(nèi)的語句次序,使得在更換圖像之后再累加imageChanges。

?

1 class PrettyMenu{ 2 ... 3 std::tr1::shared_ptr<Image> bgImage; 4 ... 5 }; 6 7 void PrettyMenu::changeBackground(std::istream& imgSrc) 8 { 9 Lock ml(&mutex); 10 bgImage.reset(new Image(imgSrc)); 11 ++imageChanges; 12 }

?

不再需要手動delete舊圖像,只有在reset在其參數(shù)(也就是“new Image(imgSrc)”的執(zhí)行結(jié)果)被成功生成之后才會被調(diào)用。美中不足的是參數(shù)imgSrc。如果Image構(gòu)造函數(shù)拋出異常,有可能輸入流的讀取記號(read marker)已被移走,而這樣的搬移對程序其余部分是一種可見的狀態(tài)改變。所以在解決這個之前只提供基本點異常安全保證。

作為策略,橋接模式或者叫做PIMPL的模式可以實現(xiàn):

PIMPL模式可以參考我的C++博客。

1 struct PMImpl{ 2 std::tr1::shared_ptr<Image> bgImage; 3 int imageChanges; 4 }; 5 class PrettyMenu{ 6 ... 7 private: 8 Mutex mutex; 9 std::tr1::shared_ptr<PMImpl> pImpl; 10 }; 11 void PrettyMenu::changeBackground(std::istream& imgSrc) 12 { 13 using std::swap; 14 Lock ml(&mutex); 15 std::tr1::shared_ptr<PMImpl> pNew(new PMImpl(*pImpl)); 16 pNew->bgImage.reset(new Image(imgSrc)); //修改副本 17 ++pNew->imageChanges; 18 swap(pImpl, pNew); //置換數(shù)據(jù) 19 }

在那個副本上做一切必要修改。若有任何修改動作拋出異常,源對象仍然保持未改變狀態(tài)。待所有改變都成功后,再將修改過的副本和原對象在一個不拋出異常的swap中置換

實現(xiàn)上通常是將所有“隸屬對象的數(shù)據(jù)”從原對象放進(jìn)另一個對象內(nèi),然后賦予源對象一個指針,指向那個所謂的實現(xiàn)對象(implementation object,即副本)。

?

◆總結(jié)

1.異常安全函數(shù)(Exception-safe functions)即時發(fā)生異常也不會泄露資源或允許任何數(shù)據(jù)結(jié)構(gòu)破壞。這樣的函數(shù)區(qū)分為三種可能的保證:基本型、強烈型、不拋異常型。

2.“強烈保證”往往能夠以copy-and-swap實現(xiàn)出來,但“強烈保證”并非對所有函數(shù)都可實現(xiàn)或具備現(xiàn)實意義。

3.函數(shù)提供的“異常安全保證”通常最高只等于其所調(diào)用之各個函數(shù)的“異常安全保證”中的最弱者。

?

轉(zhuǎn)載于:https://www.cnblogs.com/hustcser/p/4217938.html

總結(jié)

以上是生活随笔為你收集整理的[Effective C++ --029]为“异常安全”而努力是值得的的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。