【问题标题】:catch exception by pointer in C++在 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++ 编译器被允许(尽管没有义务)省略这两个复制过程,并且对于第一个复制(然后抛出),如果不省略复制则必须移动对象而不是复制。

标签: c++ exception-handling throw try-catch


【解决方案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& 捕获。
          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 2019-08-22
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2011-02-04
          相关资源
          最近更新 更多