繁体   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