1

我在堆栈上有一个对象,我希望它的析构函数在调用析构函数时跳过一些工作,因为由于堆栈上对象的范围引发了特定异常,堆栈正在展开。

现在我可以在堆栈项的范围内添加一个 try catch 块并捕获有问题的异常并通知堆栈对象不运行要跳过的工作,然后重新抛出异常,如下所示:

RAII_Class pending;

try {
  doSomeWorkThatMayThrowException();
} catch (exceptionToSkipPendingDtor &err) {
  pending.notifySkipResourceRelease();
  throw;
}

但是,我希望有一种更优雅的方式来做到这一点。例如想象:

RAII_Class::~RAII_Class {
  if (detectExceptionToSkipPendingDtorBeingThrown()) {
    return;
  }
  releaseResource();
}
4

7 回答 7

8

你几乎可以用 来做到这一点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.
于 2010-07-23T23:36:31.507 回答
5

我会反过来做——如果没有抛出异常,明确告诉它做它的工作:

RAII_Class pending;

doSomeWorkThatMayThrowException();

pending.commit(); // do or prepare actual work
于 2010-07-23T23:38:51.240 回答
3

这似乎绕过了使用 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 语义。

于 2010-07-23T23:51:43.813 回答
0

看起来像 bool std::uncaught_exception(); 如果您想对每个异常都具有此行为,而不仅仅是特殊异常,则可以解决问题!

于 2010-07-23T23:40:24.040 回答
0

你可以不用try-catch:

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;
}
于 2010-07-23T23:44:15.080 回答
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( ... ) {
  }
}

这样,您的析构函数只做一件事,并且您可以防止在异常期间抛出异常(我认为这是您要解决的问题)。

于 2010-07-23T23:53:57.537 回答
0

虽然它充其量只是一个杂物,但如果您拥有您感兴趣的异常类的代码,您可以向该类(布尔)添加一个静态数据成员,该成员将在对象的构造函数中设置为“true”该类的,在析构函数中为 false (可能需要是一个 int ,您可以改为递增/递减)。然后在您的 RAII 类的析构函数中,您可以检查 std::uncaught_exception(),如果为真,则查询异常类中的静态数据成员。如果您返回 true(或 > 0),则您遇到了这些异常之一 - 否则您将忽略它。

不是很优雅,但它可能会成功(只要你没有多个线程)。

于 2010-07-24T00:51:45.483 回答