在 C++ 中通过指针捕获异常

Posted

技术标签:

【中文标题】在 C++ 中通过指针捕获异常【英文标题】:catch exception by pointer in C++ 【发布时间】:2011-01-02 15:24:31 【问题描述】:

我发现捕获异常有三种方式,有什么区别?

1) 按价值捕获;

2) 通过引用捕获;

3) 指针捕获;

我只知道按值捕获会调用对象的两个副本,按引用捕获会调用一个。那么如何通过指针捕获呢?何时使用指针捕获?除了抛出一个对象,我可以像这样抛出一个指向对象的指针吗?

class A 

void f() 

  A *p = new A();
        throw p;



【问题讨论】:

您可以通过指针捕获异常。您可以捕获恰好是指针的异常。问题是 A 和 A* 是两种完全不同的类型。如果你抛出一个指向 A 的指针,那么你只能捕获值或引用。但你通过价值或参考而不是 A 来捕捉的是 A*。 另外,由于 C++ 编译器被允许(尽管没有义务)省略这两个复制过程,并且对于第一个复制(然后抛出),如果不省略复制则必须移动对象而不是复制。 【参考方案1】:

虽然基本上可以抛出任何类型的任何对象,但这样做并没有什么好处(如果有的话)。动态分配主要在对象需要有生命周期而不适合自动分配时很有用——即您希望它的生命周期独立于正常程序范围。

但是,在异常对象的情况下,这并没有多大意义。异常对象通常仅在异常处理程序内部使用,并且您显然希望在退出该异常的(最后一个)处理程序时将其销毁。

还有一个事实是,您通常希望异常处理代码相当简单。举个例子,如果你试图报告空闲存储/堆被耗尽或损坏,那么尝试将异常对象从耗尽/损坏的空闲存储/堆中分配出来通常不会很好......

【讨论】:

【参考方案2】:

Microsoft 的 MFC 使用 catch by pointer,但我认为这是为了在正确实现 try 和 catch 之前与编译器兼容;最初他们使用 TRY 和 CATCH 宏来模拟它。每个异常都派生自CException,它有一个方法来判断对象是否需要被删除。

对于任何现代异常设计,我都不建议这样做。通过引用捕获是要走的路。

【讨论】:

【参考方案3】:

Catch 遵循正常的赋值兼容规则,即如果你抛出一个值,你可以将它作为值或引用捕获,但不能作为指针;如果你抛出一个指针,你只能将它作为一个指针(或对指针的引用......)来捕获。

但是抛出指针并没有真正的意义,它只会引起内存管理的头痛。因此,您通常应该遵循 按值抛出,按引用捕获的规则,正如 Gregory 所解释的那样。

【讨论】:

【参考方案4】:

通过指针捕获/抛出异常并不是一个很好的场景。 C++ 语义允许这样做,但它并不是非常有用,因为大多数时候你会抛出一个临时异常或字符串对象。

但是,一些库(我相信 Boost.Graph 会这样做)使用 throw 将返回值从深度递归函数传回给调用者;在这种情况下,返回值可能是一个指针,所以抛出一个指针是有意义的。

【讨论】:

【参考方案5】:

推荐的方式是按值抛出,按引用捕获

您的示例代码抛出了一个指针,这是一个坏主意,因为您必须在 catch 站点管理内存。

如果您真的觉得应该抛出一个指针,请使用智能指针,例如shared_ptr

无论如何,Herb Sutter 和 Alexei Alexandrescu 在我转述的 C++ 编码标准一书中很好地解释了这一点。

见C++ Coding Standards: Throw by Value, Catch by Reference。

【讨论】:

如果你抛出的原因是因为你的内存不足,那么尝试分配一个新的对象来抛出是没有帮助的。 根据是否可以分配 A,您要么抛出指向 A 的指针,要么抛出 std::bad_alloc。所以至少你会扔东西...... 我见过像const std::runtime_error err; throw err; 这样的代码,假设这实际上不会被引用捕获(没有从const std::runtime_error&std::runtime_error& 的转换),我是否正确?因此,这将被运行时捕获并可能使程序崩溃。我说的对吗? @SteveJessop 这实际上是一个很好的观察。我一直担心std::string 的异常描述被复制到std::exception 构造函数中。但是,如果它无法复制,我想无论如何都会抛出一个无缘无故的std::bad_alloc。或者可能会出现无限递归:)。考虑得很周到。 @theswine 否。当抛出一个临时对象时。 [N3337, 15.1$3] 它的内存以未指定的方式分配,但不是由 operator new 分配的。 [N3337, 151.$4] throw 表达式中的任何内容都用于初始化该临时对象(通过复制 - 可能被删除 - 或通过移动)。在您的特定示例中,std::runtime_error 的临时对象将由 err 的副本创建和初始化。然后可以被 & 和 const& 捕获。

以上是关于在 C++ 中通过指针捕获异常的主要内容,如果未能解决你的问题,请参考以下文章

解决用try except 捕获assert函数产生的AssertionError异常时,导致断言失败的用例在测试报告中通过的问题

机油测试引发未捕获的异常

为啥我应该在抛出异常指针时使用按引用捕获

纠正互联网上关于捕获异常事务可提交的言论

捕获空指针异常是代码异味吗?

自定义异常