抛出新的 std::exception 与抛出 std::exception

Posted

技术标签:

【中文标题】抛出新的 std::exception 与抛出 std::exception【英文标题】:throw new std::exception vs throw std::exception 【发布时间】:2012-06-12 11:33:39 【问题描述】:

在查看一些我偶然发现的代码时:

throw /*-->*/new std::exception ("//...

我一直认为你不需要/不应该在这里使用new。 正确的方法是什么,都可以,如果可以有区别吗?

顺便说一句,我在使用 PowerShell 增强库“grepping”时所看到的从不使用 throw new

附:我还发现了一些使用 throw gcnew 的 CLI 代码。可以吗?

【问题讨论】:

我认为throw gcnew 会很有用,例如。如果您希望托管代码捕获您的异常。有人可以纠正我吗? .Net 通过指针处理异常,因此 throw gcnew 是正确的做法。 @SebastianRedl .Net “指针”可能不明确?虽然 gcnew 肯定不是。 System::Exception 通常是对垃圾收集堆上的托管对象的引用。我总是用gcnew 抛出并用System::Exception ^ 抓住。当然,我在 C++/CLI 中也一直使用 finally,尽管在同一个 try 块中不经常与 C++ 异常混合,我不知道为什么。 【参考方案1】:

传统的抛出和捕获异常的方法是抛出一个异常对象并通过引用(通常是const 引用)来捕获它。 C++ 语言要求编译器生成合适的代码来构造异常对象,并在合适的时间进行适当的清理。

将指针指向动态分配的对象绝不是一个好主意。异常应该使您能够在面对错误条件时编写更健壮的代码。如果您以常规方式抛出异常对象,您可以确定它是否被命名正确类型的 catch 子句捕获,是否被 catch (...) 捕获,无论它是否被重新抛出,它都会在适当的时候。 (唯一的例外是如果它根本没有被捕获,但无论你怎么看,这都是不可恢复的情况。)

如果你抛出一个指向动态分配对象的指针,你必须确保无论调用堆栈在你想抛出异常的时候是什么样的,都有一个 catch 块,它命名了正确的指针类型并具有适当的 @ 987654324@电话。 catch (...) 绝对不能捕获您的异常,除非该块重新抛出异常,然后被另一个正确处理异常的 catch 块捕获。

实际上,这意味着您已经采用了异常处理功能,该功能应该可以更轻松地编写健壮的代码,并且很难编写在所有情况下都正确的代码。这撇开了一个问题,即几乎不可能充当不期望此功能的客户端代码的库代码。

【讨论】:

“抛出异常对象”堆栈或堆我的朋友?堆栈还是堆? (也许我在某处看一个糟糕的全局示例)哦,如果是堆栈,那么合适的范围是什么? @ebyrob:我不太确定你在问什么,但听起来你想知道异常对象的存储和/或生命周期,可能会回答 here。如果不是,您最好单独提出一个问题。【参考方案2】:

抛出异常时无需使用new

只写:

throw yourexception(yourmessage);

并捕获为:

catch(yourexception const & e)

      //your code (probably logging related code)

请注意,yourexception 应直接或间接派生自 std::exception

【讨论】:

为什么?为什么不使用new?为什么要从std::exception 派生yourexception 当我很懒惰时(这很常见)为什么throw std::exception; 不起作用? g++ 似乎不会编译它... @ebyrob: std::exception 是一个类型,你不能抛出一个type,你必须抛出一个object。所以语法应该是这样的:throw std::exception(); 这将编译。现在这有多好,完全是一个不同的问题。【参考方案3】:

如果调用站点期望捕获std::exception*,则抛出new std::exception 是正确的。但是没有人会期望捕获指向异常的指针。即使您记录了您的函数所做的事情并且人们阅读了文档,他们仍然可能会忘记并尝试捕获对 std::exception 对象的引用。

【讨论】:

投掷new std::exception只有在调用站点期望捕获一个指针并且期望接管分配异常的管理并且永远不会有任何情况下你是正确的将被未明确捕获正确指针(catch(...) 或根本不处理)的东西调用,否则将出现对象泄漏。简而言之,这可以近似为“从不”。 很好奇这个答案是如何被接受的,而真正正确的答案是@CharlesBailey 的comment @John:我也想到了。但我认为一二拳有很好的效果,我给出了枯燥的总结,查尔斯有趣地扩展了人们容易忘记正确处理它的各种方式。可惜你没有从投票赞成的 cmets 中获得声誉。 Charles 没有提供他的答案,而这个 A(与另一个不同)在 A 和评论中都有解释。【参考方案4】:

C++ FAQ 对此有很好的讨论:

    https://isocpp.org/wiki/faq/exceptions#what-to-catch https://isocpp.org/wiki/faq/exceptions#catch-by-ptr-in-mfc

基本上“除非有充分的理由不按引用捕获。避免按值捕获,因为这会导致生成副本,并且副本的行为可能与抛出的行为不同。只有在非常特殊的情况下才应该捕获通过指针。”

【讨论】:

像往常一样,FAQ 的措辞很糟糕。您可以按值或引用捕获。指针恰好是一个值(您通过值或引用捕获)。请记住A 类型与A* 类型不同,所以如果我这样做throw A(),我无法捕捉catch(A* e),因为它是完全不同的类型。 这些链接现在已损坏。 我修复了链接@spanndemic【参考方案5】:

运算符 new 不能保证它永远不会引发异常。出于这个原因,使用它来引发“有效”(预期)异常会产生无法保证不会崩溃的代码。由于一次可能只有一个异常,并且您的程序尝试在捕获其中任何一个之前抛出两个异常,因此实现可以做的最好的事情是立即中止您的程序,例如通过调用 std::terminate。

【讨论】:

以上是关于抛出新的 std::exception 与抛出 std::exception的主要内容,如果未能解决你的问题,请参考以下文章

我可以/我应该使用 std::exception 进行常规错误处理吗?

T-SQL编程中的异常处理-异常捕获(try catch)与抛出异常(throw)

“抛出”和“抛出新异常()”之间的区别

iOS:如何获取未处理的 std::exception 的堆栈跟踪?

有没有办法将“抛出新的异常()”挤压到一个对象中?

在抛出std :: exception的实例后终止调用