C++中异常对象的作用域范围

45

C++中异常对象的作用域是什么?它是否在catch处理程序执行后立即超出作用域?此外,如果我创建一个未命名的异常对象并将其抛出,那么在捕获该异常时,通过const引用还是非const引用捕获有关系吗?


3
你是在询问“寿命”吗? - Joren
是的,它什么时候会被销毁? - Naveen
10
为了澄清Joren的问题:术语“scope”通常指变量拥有名称的区域(代码行)。单词“scope”经常被错误地用来表示“lifetime”,也就是变量实际驻留在内存中的时间,正如你所理解的那样。 - Thomas
2
谢谢Thomas,这也符合标准(n4296)3.8“对象的生命周期是运行时属性”,3.3“每个特定名称仅在称为其作用域的某些可能不连续的程序文本部分内有效” - 因此,作用域指的是源代码。我从未想过这种区别。 - Kit10
3个回答

44
当评估一个throw表达式时,会从表达式的值初始化异常对象。抛出的异常对象的类型取决于throw表达式的静态类型,忽略任何const和volatile限定符。对于类类型,这意味着执行复制初始化。
异常对象的作用域在抛出异常的块的作用域之外。可以将其视为生活在正常调用堆栈旁边的特殊异常区域中,其中本地对象存在。
在catch块内,与捕获异常对象初始化的名称被初始化为此异常对象,而不是throw语句的参数,即使它是lvalue。
如果通过非const引用catch,则可以更改异常对象,但不能更改其初始化内容。如果以值或const引用方式进行catch(除去const_cast),您无法通过重新抛出异常来更改程序行为。
当最后一个不通过重新抛出(即无参throw表达式求值)退出的catch块完成时,异常对象将被销毁。

最后一个catch块是什么? catch(...){std :: exception * p = nullptr;尝试{throw; } catch(const std :: exception&e){p =&e; } catch(...){} if(p){std :: cerr << p-> what()<< std :: endl; }}。这是有效的代码吗?内部catch不通过重新抛出退出。外部catch(...)是最后一个块吗? - anton_rh

9

异常对象仅在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块内部。 - Konrad Rudolph

4

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


将指针抛给MFC中的某个东西怎么样?除了问题和答案中暗示的类类型对象,您还需要考虑其他对象。 - Sam
指针也会被复制,但通常没人关心 :) - vava

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