[英]Way for C++ destructor to skip work when specific exception being thrown?
我在堆棧上有一個對象,我希望它的析構函數在調用析構函數時跳過一些工作,因為由於在堆棧上對象范圍內引發了特定異常而使堆棧未解繞。
現在,我可以在堆棧項的范圍內添加一個try catch塊,並捕獲有問題的異常,並通知堆棧對象不運行要跳過的工作,然后按如下所示重新拋出異常:
RAII_Class pending;
try {
doSomeWorkThatMayThrowException();
} catch (exceptionToSkipPendingDtor &err) {
pending.notifySkipResourceRelease();
throw;
}
但是,我希望有一種更優雅的方法來做到這一點。 例如,假設:
RAII_Class::~RAII_Class {
if (detectExceptionToSkipPendingDtorBeingThrown()) {
return;
}
releaseResource();
}
您幾乎可以使用std::uncaught_exception()
來做到這一點,但還不完全如此。
赫伯·薩特(Herb Sutter)比我更好地解釋了“幾乎”: http : //www.gotw.ca/gotw/047.htm
在某些極端情況下,從析構函數調用std::uncaught_exception()
返回true,但是所討論的對象實際上並未被堆棧展開過程破壞。
沒有RAII,您可能會更好,因為它與您的用例不匹配。 RAII意味着要經常清理; 是否例外。
您想要的東西要簡單得多:僅在不引發異常(這是簡單的功能序列)的情況下才釋放資源。
explicitAllocateResource();
doSomeWorkThatMayThrowException();
explicitReleaseResource(); // skipped if an exception is thrown
// by the previous function.
我會用另一種方式來做-如果沒有拋出異常,則明確地告訴它進行工作:
RAII_Class pending;
doSomeWorkThatMayThrowException();
pending.commit(); // do or prepare actual work
這似乎繞開了使用RAII的主要原因。 RAII的要點是,如果代碼中間發生異常,您仍然可以釋放資源/對其進行適當的銷毀。
如果這不是您想要的語義,請不要使用RAII。
所以代替:
void myFunction() {
WrapperClass wc(acquireResource());
// code that may throw
}
做就是了:
void myFunction() {
Resource r = acquireResource();
// code that may throw
freeResource(r);
}
如果中間的代碼拋出了,資源將不會被釋放。 這就是您想要的,而不是保留RAII(並保留名稱)而不實現RAII語義。
看起來像布爾std :: uncaught_exception(); 如果您想讓每個異常(不僅是特殊異常)都具有這種行為,該技巧就可以了!
您可以不用嘗試就可以做到:
RAII_Class pending;
doSomeWorkThatMayThrowException(); // intentional: don't release if throw
pending.releaseResource();
另外,您可以嘗試使用RAII進行一些嘗試:
struct RAII_Class {
template<class Op>
void execute(Op op) {
op();
releaseResources();
}
private:
void releaseResources() { /* ... */ }
};
int main(int argc, char* argv[])
{
RAII_Class().execute(doSomeWorkThatMayThrowException);
return 0;
}
我在這個網站上找到了有關std :: uncaught_exception()的有趣討論,以及對您的問題的替代解決方案,該解決方案對我而言似乎更為優雅和正確:
http://www.gotw.ca/gotw/047.htm
// Alternative right solution
//
T::Close() {
// ... code that could throw ...
}
T::~T() /* throw() */ {
try {
Close();
} catch( ... ) {
}
}
這樣,您的析構函數只會做一件事,並且可以防止在異常期間引發異常(我認為這是您要解決的問題)。
盡管充其量只是一個麻煩,但如果您擁有自己感興趣的異常類的代碼,則可以向該類(布爾)添加一個靜態數據成員,該成員將在對象的構造函數中設置為“ true”該類的值,在析構函數中為false(可能需要是一個int,而應增加/減少)。 然后,在RAII類的析構函數中,可以檢查std :: uncaught_exception(),如果為true,則查詢異常類中的靜態數據成員。 如果返回true(或> 0),則說明其中一種例外-否則將其忽略。
不是很優雅,但是它可能會解決問題(只要您沒有多個線程)。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.