簡體   English   中英

當拋出特定異常時,C ++析構函數跳過工作的方法是什么?

[英]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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM