C++核心準(zhǔn)則?討論:析構(gòu),釋放和交換操作必須永不失敗

Discussion: Destructors, deallocation, and swap must never fail
討論:析構(gòu),釋放和交換操作必須永不失敗
Never allow an error to be reported from a destructor, a resource deallocation function (e.g.,?operator delete), or a?swap?function using?throw. It is nearly impossible to write useful code if these operations can fail, and even if something does go wrong it nearly never makes any sense to retry. Specifically, types whose destructors might throw an exception are flatly forbidden from use with the C++ Standard Library. Most destructors are now implicitly?noexcept?by default.
永遠(yuǎn)不要允許從析構(gòu)函數(shù),資源釋放函數(shù)(例如,運(yùn)算符刪除)或交換函數(shù)中使用throw報(bào)告錯(cuò)誤。如果這些操作失敗,編寫有用的代碼幾乎是不可能的,發(fā)生錯(cuò)誤,重試也幾乎沒有任何意義。特別是,析構(gòu)函數(shù)可能引發(fā)異常的類型已經(jīng)被明確禁止與C ++標(biāo)準(zhǔn)庫一起使用?,F(xiàn)在默認(rèn)情況下,大多數(shù)析構(gòu)函數(shù)都隱式地為noexcept。
Example(示例)
class Nefarious {
public:
Nefarious() { /* code that could throw */ } // ok
~Nefarious() { /* code that could throw */ } // BAD, should not throw
// ...
};
Nefarious?objects are hard to use safely even as local variables:
邪惡對(duì)象很難安全地用作局部變量:
void test(string& s)
{
Nefarious n; // trouble brewing
string copy = s; // copy the string
} // destroy copy and then nHere, copying?s?could throw, and if that throws and if?n's destructor then also throws, the program will exit via?std::terminate?because two exceptions can't be propagated simultaneously.
在這里,復(fù)制s可能會(huì)拋出異常,而且拋出異常時(shí),如果n的析構(gòu)函數(shù)也拋出異常,則程序?qū)⑼ㄟ^std :: terminate退出,因?yàn)閮蓚€(gè)異常不能同時(shí)傳播。
Classes with?Nefarious?members or bases are also hard to use safely, because their destructors must invoke?Nefarious' destructor, and are similarly poisoned by its bad behavior:
具有Nefarious成員或基類的類也很難安全地使用,因?yàn)樗鼈兊奈鰳?gòu)函數(shù)必須調(diào)用Nefarious的析構(gòu)函數(shù),并且類似地會(huì)因其不良行為而中毒:
class Innocent_bystander {
Nefarious member; // oops, poisons the enclosing class's destructor
// ...
};
void test(string& s)
{
Innocent_bystander i; // more trouble brewing
string copy2 = s; // copy the string
} // destroy copy and then iHere, if constructing?copy2?throws, we have the same problem because?i's destructor now also can throw, and if so we'll invoke?std::terminate.
在這里,如果copy2的構(gòu)造過程拋出異常,我們將遇到相同的問題,因?yàn)槲业奈鰳?gòu)函數(shù)現(xiàn)在也可能拋出異常,如果是,std :: terminate將會(huì)被觸發(fā)。
You can't reliably create global or static?Nefarious?objects either:
您不能可靠地創(chuàng)建全局或靜態(tài)Nefarious對(duì)象:
static Nefarious n; // oops, any destructor exception can't be caughtYou can't reliably create arrays of?Nefarious:
您無法可靠地創(chuàng)建Nefarious數(shù)組:
void test()
{
std::arrayarr; // this line can std::terminate(!)
}The behavior of arrays is undefined in the presence of destructors that throw because there is no reasonable rollback behavior that could ever be devised. Just think: What code can the compiler generate for constructing an?arr?where, if the fourth object's constructor throws, the code has to give up and in its cleanup mode tries to call the destructors of the already-constructed objects ... and one or more of those destructors throws? There is no satisfactory answer.
如果存在引發(fā)異常的析構(gòu)函數(shù),數(shù)組的行為是不確定的,因?yàn)闆]有合理的回滾行為可以設(shè)計(jì)。試想一下:編譯器可以生成什么代碼來構(gòu)造arr,如果第四個(gè)對(duì)象的構(gòu)造函數(shù)拋出該代碼,則該代碼必須放棄,并在其清理模式下嘗試調(diào)用已構(gòu)造對(duì)象的析構(gòu)函數(shù)...這些更多的析構(gòu)函數(shù)會(huì)拋出異常么?沒有令人滿意的答案。
You can't use?Nefarious?objects in standard containers:
您不能在標(biāo)準(zhǔn)容器中使用Nefarious對(duì)象:
std::vectorvec(10); // this line can std::terminate() The standard library forbids all destructors used with it from throwing. You can't store?Nefarious?objects in standard containers or use them with any other part of the standard library.
標(biāo)準(zhǔn)庫禁止所有與其一起使用的析構(gòu)函數(shù)拋出異常。您不能將Nefarious對(duì)象存儲(chǔ)在標(biāo)準(zhǔn)容器中,也不能將它們與標(biāo)準(zhǔn)庫的任何其他部分一起使用。
Note(注意)
These are key functions that must not fail because they are necessary for the two key operations in transactional programming: to back out work if problems are encountered during processing, and to commit work if no problems occur. If there's no way to safely back out using no-fail operations, then no-fail rollback is impossible to implement. If there's no way to safely commit state changes using a no-fail operation (notably, but not limited to,?swap), then no-fail commit is impossible to implement.
這些是必不可少的關(guān)鍵功能,因?yàn)樗鼈兪鞘聞?wù)編程中兩個(gè)關(guān)鍵操作所必需的:如果在處理過程中遇到問題,則回滾工作;如果沒有問題,則提交工作。如果無法使用無失敗操作安全地退出,則無失敗回滾是不可能實(shí)現(xiàn)的。如果無法使用無失敗操作(特別是但不限于交換)來安全地提交狀態(tài)更改,那么就不可能實(shí)現(xiàn)無失敗提交。
Consider the following advice and requirements found in the C++ Standard:
考慮C ++標(biāo)準(zhǔn)中的以下建議和要求:
If a destructor called during stack unwinding exits with an exception, terminate is called (15.5.1). So destructors should generally catch exceptions and not let them propagate out of the destructor. --[C++03]?§15.2(3)
如果在堆棧展開期間調(diào)用的析構(gòu)函數(shù)異常退出,則將終止(15.5.1)。因此,析構(gòu)函數(shù)通常應(yīng)捕獲異常,而不應(yīng)讓它們傳播出析構(gòu)函數(shù)。-[C ++ 03]§15.2(3)
No destructor operation defined in the C++ Standard Library (including the destructor of any type that is used to instantiate a standard-library template) will throw an exception. --[C++03]?§17.4.4.8(3)
C ++標(biāo)準(zhǔn)庫中定義的析構(gòu)函數(shù)操作(包括用于實(shí)例化標(biāo)準(zhǔn)庫模板的任何類型的析構(gòu)函數(shù))都不會(huì)引發(fā)異常。-[C ++ 03]§17.4.4.8(3)
Deallocation functions, including specifically overloaded?operator delete?and?operator delete[], fall into the same category, because they too are used during cleanup in general, and during exception handling in particular, to back out of partial work that needs to be undone. Besides destructors and deallocation functions, common error-safety techniques rely also on?swap?operations never failing -- in this case, not because they are used to implement a guaranteed rollback, but because they are used to implement a guaranteed commit. For example, here is an idiomatic implementation of?operator=?for a type?T?that performs copy construction followed by a call to a no-fail?swap:
取消分配功能(特別包括重載的運(yùn)算符delete和operator delete [])屬于同一類,因?yàn)樗鼈兺ǔT谇謇磉^程中(尤其是在異常處理過程中)也用于撤消需要撤消的部分工作。除了析構(gòu)函數(shù)和釋放函數(shù)之外,常見的安全的錯(cuò)誤處理技術(shù)還依賴于永不失敗的交換操作-在這種情況下,不是因?yàn)樗鼈冇糜趯?shí)現(xiàn)有保證的回滾,而是因?yàn)樗鼈冇糜趯?shí)現(xiàn)有保證的提交。例如,以下是對(duì)類型T的operator =的慣用實(shí)現(xiàn),該類型T執(zhí)行拷貝構(gòu)造,然后調(diào)用無失敗交換:
T& T::operator=(const T& other)
{
auto temp = other;
swap(temp);
return *this;
}
(See also Item 56. ???)
(另請(qǐng)參閱第56項(xiàng)。)
Fortunately, when releasing a resource, the scope for failure is definitely smaller. If using exceptions as the error reporting mechanism, make sure such functions handle all exceptions and other errors that their internal processing might generate. (For exceptions, simply wrap everything sensitive that your destructor does in a?try/catch(...)?block.) This is particularly important because a destructor might be called in a crisis situation, such as failure to allocate a system resource (e.g., memory, files, locks, ports, windows, or other system objects).
幸運(yùn)的是,釋放資源時(shí),失敗的范圍肯定較小。如果使用異常作為錯(cuò)誤報(bào)告機(jī)制,請(qǐng)確保此類函數(shù)處理其內(nèi)部處理可能生成的所有異常和其他錯(cuò)誤。(對(duì)于例外情況,只需將您的析構(gòu)函數(shù)所做的所有敏感操作都包裝在try / catch(...)塊中。)這尤其重要,因?yàn)樵谖C(jī)情況下可能會(huì)調(diào)用析構(gòu)函數(shù),例如無法分配系統(tǒng)資源(例如,,內(nèi)存,文件,鎖,端口,窗口或其他系統(tǒng)對(duì)象)。
When using exceptions as your error handling mechanism, always document this behavior by declaring these functions?noexcept. (See Item 75.)
當(dāng)使用異常作為錯(cuò)誤處理機(jī)制時(shí),請(qǐng)始終通過聲明這些函數(shù)noexcept來說明此類行為。(請(qǐng)參閱第75條。)
原文鏈接https://github.com/isocpp/CppCoreGuidelines/blob/master/CppCoreGuidelines.md#discussion-destructors-deallocation-and-swap-must-never-fail
新書介紹
《實(shí)戰(zhàn)Python設(shè)計(jì)模式》是作者最近出版的新書,拜托多多關(guān)注!

本書利用Python 的標(biāo)準(zhǔn)GUI 工具包tkinter,通過可執(zhí)行的示例對(duì)23 個(gè)設(shè)計(jì)模式逐個(gè)進(jìn)行說明。這樣一方面可以使讀者了解真實(shí)的軟件開發(fā)工作中每個(gè)設(shè)計(jì)模式的運(yùn)用場景和想要解決的問題;另一方面通過對(duì)這些問題的解決過程進(jìn)行說明,讓讀者明白在編寫代碼時(shí)如何判斷使用設(shè)計(jì)模式的利弊,并合理運(yùn)用設(shè)計(jì)模式。
對(duì)設(shè)計(jì)模式感興趣而且希望隨學(xué)隨用的讀者通過本書可以快速跨越從理解到運(yùn)用的門檻;希望學(xué)習(xí)Python GUI 編程的讀者可以將本書中的示例作為設(shè)計(jì)和開發(fā)的參考;使用Python 語言進(jìn)行圖像分析、數(shù)據(jù)處理工作的讀者可以直接以本書中的示例為基礎(chǔ),迅速構(gòu)建自己的系統(tǒng)架構(gòu)。
覺得本文有幫助?請(qǐng)分享給更多人。
關(guān)注微信公眾號(hào)【面向?qū)ο笏伎肌枯p松學(xué)習(xí)每一天!
面向?qū)ο箝_發(fā),面向?qū)ο笏伎迹?/span>
