3

我知道析构函数不应该抛出异常。

http://www.parashift.com/c++-faq-lite/dtors.html#faq-11.13

我有以下代码:

~a()
{
    cleanup();
}

// I do not expect exception being thrown in this function.
// If exception really happen, I know that it is something not recoverable.
void a::cleaup()
{
    delete p;
}

在我的静态源代码分析中,它抱怨我将以这种方式调用清理函数:

~a()
{
    try {
        cleanup();
    }
    catch(...) {
        // What to do? Print out some logging message?
    }
}

// I do not expect exception being thrown in this function.
// If exception really happen, I know that it is something not recoverable.
void a::cleaup()
{
    delete p;
}

我不确定这是否是一个好习惯,只要它调用函数,就将 try...catch 块放在析构函数中。作为 :

(1) 如果清理函数能够抛出异常,我就知道发生了不好的事情。我更喜欢它是快速失败的。也就是让整个系统崩溃,让程序员去调试。

(2) 进入和退出 try...catch 块时发生开销。

(3) 代码看起来很麻烦,在类的析构函数周围有很多 try...catch 块。

我可能会错过其他一些要点,为什么 try...catch 块应该到位。

谢谢。

4

6 回答 6

3

由于delete不会抛出,所以也不会cleanup,因此没有必要将调用置于 try-catch 中。

由于您的静态分析工具可能很难弄清楚这一点,也许您可​​以通过声明cleanup为 no-throw 来帮助它(虽然这只是一个猜测)。

void cleanup() throw();
于 2009-12-07T09:36:49.320 回答
2

也许您可以更精确地了解您的清理功能。cleanup() 函数是否会故意抛出异常,即使只是在极少数情况下?那么你绝对应该在你的析构函数中处理这种情况,因为即使很少见,异常抛出也是 cleanup() 的预期行为。

至少,您的异常处理代码可以使您的程序处于定义良好的状态,从而允许您优雅地结束您的程序。

或者你的意思是 cleanup() 可能会抛出一个 OutOfMemoryException 或另一个你从未在其他任何地方处理过的运行时异常?然后我也会省略析构函数中的异常处理。在这种不寻常的情况下,您的应用程序可能无论如何都无法运行其错误处理代码。您应该尽一切可能确保即使是这样的异常也能被正确报告或记录。

编辑:

现在您已经展示了 的实现cleanup,Neil 已经回答了您的问题。delete p不能抛出,因此a::cleanup只要p' 的析构函数不抛出就不会抛出。

于 2009-12-07T08:48:55.517 回答
1

当然,问题确实出在 p 的析构函数中。当你在 cleanup() 中说

delete p;

delete 本身不能抛出,因此异常必须来自 p 的析构函数。正是在那个析构函数中,您需要担心使用 try 块,而不是 a 块。

于 2009-12-07T09:07:44.507 回答
1

从根本上说,在已经处理异常的同时抛出异常是不安全的。

如果您的析构函数在堆栈展开时被调用,并且引发了另一个异常,那么您的线程可以在某些系统(例如 Symbian)上立即终止;您的代码计划捕获异常不会停止该终止 - 您的代码甚至不会被调用。如果您的异常逃脱了析构函数,您的线程肯定会被终止。

所以try {} catch(...) {}在析构函数中是不可移植的,而且肯定很棘手。

合理的建议是永远不要调用可能在清理代码中引发异常的代码,例如析构函数和任何可能从析构函数调用的函数,例如' cleanup()'、' close()'或' release_something()'。

原发帖人还查询了try-catch的性能。在采用 C++ 异常的早期,异常处理代码相当昂贵。如今,您的编译器几乎可以肯定使用零成本异常,这意味着未抛出的异常不会对代码增加运行时性能损失(但当然会略微增加程序的二进制大小)。

于 2009-12-07T09:12:11.610 回答
1

由于内存分配或删除而引发错误通常没有任何意义。它要么工作要么不工作,你可以花费大量的工作来处理愚蠢的错误而没有得到任何好处。

您可能希望在特定情况下执行此操作,但在实践中并没有做太多事情,并且当有问题时,您通常希望使用调试器或更完整的工具。

于 2009-12-07T09:18:54.033 回答
0

我从不使用它,但异常安全纯粹主义者会说你的cleanup()函数可能会在你意想不到的情况下抛出。所以这种模式是一种额外安全的技术。

但是,cleanup函数不应该首先抛出。所以我会把任何异常处理放在里面cleanup(),而不是在析构函数本身。

于 2009-12-07T08:41:33.217 回答