C++ 的方式抛出特定异常时析构函数跳过工作?

发布于 2024-09-10 20:59:46 字数 520 浏览 4 评论 0原文

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

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

RAII_Class pending;

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

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

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

I have an object on the stack for which I wish its destructor to skip some work when the destructor is being called because the stack is being unwound due to a specific exception being thrown through the scope of the object on the stack.

Now I could add a try catch block inside the scope of the stack item and catch the exception in question and notify the stack object to not run the work to be skipped an then rethrow the exception as follows:

RAII_Class pending;

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

However, I'm hoping there is a more elegant way to do this. For example imagine:

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

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(7

生生不灭 2024-09-17 20:59:46

您几乎可以使用 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.

You can almost do this with std::uncaught_exception(), but not quite.

Herb Sutter explains the "almost" better than I do: http://www.gotw.ca/gotw/047.htm

There are corner cases where std::uncaught_exception() returns true when called from a destructor but the object in question isn't actually being destroyed by the stack unwinding process.

You're probably better off without RAII because it doesn't match your use case. RAII means always clean up; exception or not.

What you want is much simpler: only release resource if an exception is not throw which is a simple sequence of functions.

explicitAllocateResource();
doSomeWorkThatMayThrowException();
explicitReleaseResource(); // skipped if an exception is thrown
                           // by the previous function.
海的爱人是光 2024-09-17 20:59:46

我会用另一种方式来做——如果没有抛出异常,明确地告诉它去做它的工作:

RAII_Class pending;

doSomeWorkThatMayThrowException();

pending.commit(); // do or prepare actual work

I would do it the other way around - explicitly tell it to do its work if no exception was thrown:

RAII_Class pending;

doSomeWorkThatMayThrowException();

pending.commit(); // do or prepare actual work
檐上三寸雪 2024-09-17 20:59:46

这似乎是规避使用 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 语义。

This seems to circumvent the main reason to use RAII. The point of RAII is that if an exception happens in the middle of your code you can still release resources/be destructed properly.

If this isn;t the semantic you want, then don't use RAII.

So instead of:

void myFunction() {
    WrapperClass wc(acquireResource());

    // code that may throw
}

Just do:

void myFunction() {
    Resource r = acquireResource();

    // code that may throw

    freeResource(r);
}

If the code in the middle throws, the resource won't be freed. This is what you want, rather than keeping RAII (and keeping the name) but not implementing RAII semantics.

雅心素梦 2024-09-17 20:59:46

看起来像 bool std::uncaught_exception();如果您希望对每个异常(而不仅仅是特殊异常)都有这种行为,那么就可以实现这一点!

Looks like bool std::uncaught_exception(); does the trick if you want to have this behavior for every exception, not just special ones!

只是在用心讲痛 2024-09-17 20:59:46

您可以不使用 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;
}

You can do without a try-catch:

RAII_Class pending;
doSomeWorkThatMayThrowException();  // intentional: don't release if throw
pending.releaseResource();

Alternatively, you can try a little harder with 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;
}
德意的啸 2024-09-17 20:59:46

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

不是很优雅,但它可能会达到目的(只要你没有多个线程)。

Although it would be a kludge at best, if you own the code for the exception class you're interested in, you could add a static data member to that class (bool) that would be set to "true" in the constructor for objects of that class, and false in the destructor (might need to be an int that you increment/decrement instead). Then in the destructor of your RAII class, you can check std::uncaught_exception(), and if true, query the static data member in your exception class. If you get true (or > 0) back, you've got one of those exceptions--otherwise you ignore it.

Not very elegant, but it would probably do the trick (as long as you don't have multiple threads).

无尽的现实 2024-09-17 20:59:46

我发现这个网站有一个关于 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( ... ) {
  }
}

通过这种方式,你的析构函数只做一件事,并且可以防止在一个例外(我认为这是您要解决的问题)。

I found this website with an interesting discussion about std::uncaught_exception() and an alternative solution to your question that seems much more elegant and correct to me:

http://www.gotw.ca/gotw/047.htm

//  Alternative right solution
//
T::Close() {
  // ... code that could throw ...
}

T::~T() /* throw() */ {
  try {
    Close();
  } catch( ... ) {
  }
}

In this way you're destructor does only one thing and you're protected against throwing an exception during an exception (which I assume is the problem you're trying to solve).

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文