【问题标题】:Scope of exception object in C++C++中异常对象的范围
【发布时间】:2010-12-11 20:49:33
【问题描述】:

C++ 中异常对象的作用域是什么?一旦执行 catch 处理程序,它是否会超出范围?另外,如果我创建一个未命名的异常对象并抛出它,那么在捕获该异常时,我是通过 const 引用还是非 const 引用来捕获它是否重要?

【问题讨论】:

  • 你问的是生命周期吗?
  • 是的..什么时候销毁?
  • 为了澄清 Joren 的问题:术语 范围 通常是指变量具有名称的区域(代码行)。 scope 这个词经常被误用来表示 lifetime,正如您所理解的,即变量实际驻留在内存中的时间。
  • 感谢 Thomas,这也符合标准 (n4296) 3.8“对象的生命周期是运行时属性”,3.3“每个特定名称仅在调用的程序文本的某些可能不连续的部分内有效它的范围” - 因此,范围是指源代码。我从来没有想过这种区别。

标签: c++ exception-handling scope


【解决方案1】:

当评估throw 表达式时,会根据表达式的值初始化异常对象。抛出的异常对象从 throw 表达式的静态类型中获取其类型,忽略任何 constvolatile 限定符。对于类类型,这意味着执行复制初始化

异常对象的范围超出了发生抛出的块的范围。把它想象成一个特殊的异常区域,位于本地对象所在的正常调用堆栈的一侧。

catch 块内,使用捕获的异常对象初始化的名称是使用此异常对象初始化的,而不是 throw 的参数,即使这是一个左值。

如果您通过非常量引用catch,那么您可以改变异常对象,但不能改变它的初始化对象。如果您重新抛出异常,您可以改变程序的行为,而如果您通过值或常量引用捕获(const_casts 除外)则无法做到。

当最后一个没有通过重新抛出(即无参数抛出表达式求值)退出的 catch 块完成时,异常对象被销毁。

【讨论】:

  • 什么是最后一个catch块catch(...) { std::exception *p = nullptr; try { throw; } catch (const std::exception &e) { p = &e; } catch(...) {} if (p) { std::cerr << p->what() << std::endl; } } 这是有效的代码吗?内部捕获不会通过重新抛出而退出。外部 catch(...) 是最后一个块吗?
【解决方案2】:

异常对象仅在catch 块中可用。您不能在 catch 块之外使用异常对象。当您抛出异常并捕获时,会发生以下步骤:

try
{
 MyException anObject;
 throw anObject;  //1

}
catch(MyException exObject)
{
}
  • throw 子句 (//1) 接收本地对象 anObject,并将其视为值参数:它创建 anObject 的副本。
  • catch 处理程序捕获一个 MyException 对象,它又是一个值参数。此时会创建另一个副本。
  • 如果 catch 处理程序已实现以接收对对象 (catch (MyException &o)) 的引用,则避免第二个副本。
  • 如果catch处理程序通过const&接收到异常对象,那么你只能调用const方法。

【讨论】:

  • throw 语句之外创建对象是一个非常糟糕的主意,正是因为这可能导致冗余副本。请改用throw Myexception();。除此之外,如果您声明一个带有名称的异常对象(如本答案所示),那么 当然 可以在声明它的范围内的 catch 块之外访问它 - 在这种情况下,在try 块内。
【解决方案3】:

首先,您抛出的对象几乎立即超出范围。异常处理程序将捕获的是原始对象的副本。在执行 catch 处理程序后,该副本将被删除除非您通过值(而不是通过引用)捕获它。在这种情况下,将创建另一个副本。但是无论如何你都应该通过引用来捕获它(最好是 const one)。

【讨论】:

  • 根据 MFC 抛出一个指向某物的指针怎么样?您需要考虑问题和答案中隐含的类类型对象以外的对象。
  • 指针也会被复制,但通常没人关心 :)
猜你喜欢
  • 2014-03-06
  • 2015-11-05
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多