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

131

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

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

而我始终认为在这里不需要/不应该使用new
正确的方法是什么,两者都可以吗?如果可以,是否有任何区别?

BTW,从我用 PowerShell 进行 "grep" 的结果来看,boost 库从不使用throw new

P.S.我还发现一些 CLI 代码使用throw gcnew。那样可以吗?


1
我认为throw gcnew会很有用,例如当您希望托管代码捕获您的异常时。有人可以纠正我吗? - jpalecek
1
.Net 处理异常时使用指针,因此在那里抛出 gcnew 是正确的做法。 - Sebastian Redl
1
@SebastianRedl .Net中,“指针”可能会产生歧义?但gcnew肯定不会。System::Exception通常是指垃圾收集堆上的托管对象。我总是用gcnew抛出,用System::Exception ^捕获。当然,在C++/CLI中我经常使用finally,尽管我不经常在同一个try块中混合使用C++异常,但我不确定为什么。 - user645280
5个回答

104
传统的抛出和捕获异常的方式是通过抛出一个异常对象并通过引用(通常是const引用)捕获它。C++语言要求编译器生成适当的代码来构造异常对象并在适当的时间正确清理它。
抛出指向动态分配对象的指针从来不是一个好主意。异常应该使您能够在错误条件下编写更加健壮的代码。如果按照传统方式抛出异常对象,您可以确信无论是通过命名正确类型的catch子句,还是通过catch(...),无论它是否被重新抛出,它都将在适当的时候正确销毁。(唯一的例外是如果它根本没有被捕获,但无论如何这都是不可恢复的情况。)
如果您抛出指向动态分配对象的指针,则必须确保无论您想要抛出异常的调用堆栈长成什么样子,在那里有一个命名了正确指针类型并具有适当的delete调用的catch块。除非该块重新抛出异常并被另一个捕获块正确处理异常,否则您的异常绝不能被catch(...)所捕获。
实际上,这意味着您已经使用应该使编写健壮代码变得更容易的异常处理功能,并使其在所有情况下编写正确的代码变得非常困难。这还不包括它几乎不可能作为库代码对客户端代码的特性进行操作的问题。

1
抛出异常对象,堆栈还是堆?堆栈还是堆?(也许我在某个全局示例中看到了错误的内容)如果是堆栈,那么适当的作用域是什么? - user645280
@ebyrob:我不太确定你在问什么,但听起来你想了解异常对象的存储和/或生命周期,这可能可以在这里得到回答(https://dev59.com/_nI-5IYBdhLWcg3w0cKG)。如果不是,你最好提出一个单独的问题。 - CB Bailey

37

在抛出异常时不需要使用new

只需写:

throw yourexception(yourmessage);

并捕获为:

catch(yourexception const & e)
{
      //your code (probably logging related code)
}

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


7
为什么不使用new?为什么要将你的异常从std::exception派生出来? - Walter
当我懒惰的时候(这种情况太常见了),为什么 throw std::exception; 无法工作?g++ 似乎无法编译它... - user645280
7
std::exception 是一种类型,你不能抛出一个 类型,而是必须抛出一个 对象。所以语法应该是 throw std::exception(); 这样就可以编译了。但它的好坏如何,则是完全不同的问题。 - Nawaz

23

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


31
如果调用方希望捕获指针并接管分配的异常,而且永远不会发生任何情况下您的函数会被未明确捕获正确指针的代码调用(catch(...)或者没有处理),那么抛出 new std::exception 才是正确的做法,否则将导致对象泄漏。简而言之,这几乎是不可能的。 - CB Bailey
有趣的是,这个答案被接受了,但实际上是@CharlesBailey的评论才是正确的答案。 - John Dibling
@John:我也有这个想法。但是我认为一二连击对我很有效,因为我提供干燥的摘要,而查尔斯则有趣地扩展了人们可能会忘记正确处理它的各种方式。不过可惜的是,你不能从点赞的评论中获得声望。 - user1084944
Charles没有提供他的答案,而这个A(与另一个不同)在A和评论中都有解释。 - NoSenseEtAl

9

3
通常FAQ措辞不佳。您可以通过值或引用来捕获。指针只是一个值(您可以通过值或引用捕获该值)。请记住,类型"A"与类型"A *"不同,因此如果我执行"throw A()",则无法使用"catch(A * e)"捕获,因为它是完全不同的类型。 - Martin York
这些链接现在已经失效。 - stephenspann
1
我修复了@spanndemic的链接。 - user1202136

2

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


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接