C ++异常catch子句

时间:2011-07-06 04:55:16

标签: c++

  

可能重复:
  Scope of exception object in C++

我有以下catch子句:

catch(Widget w);
catch(Widget& w);

void passAndThrowWidget() {
          Widget localWidget;
      throw localWidget;
}

如果我们按值捕获Widget对象,编译器将进行复制,因此当我们抛出异常时,localWidget超出范围,我们看不到任何问题。

如果我们通过引用捕获widget对象,根据参考概念,“w”指向相同的本地Widget而不是副本。但我已经看到大多数异常都是由C ++中的引用捕获的。我的问题是如何工作“localWidget”在抛出异常时超出范围,并通过引用捕获指向被破坏的对象。

谢谢!

5 个答案:

答案 0 :(得分:9)

throw expr;return expr;类似,因为它使用复制初始化(使用C ++ 0x也可以进行列表初始化)。但那是(大部分)语法。

说到语义,那么就像从返回非引用类型的函数返回一个值就好了,抛出就是这样:

T f()
{
    // t is local but this is clearly fine
    T t;
    return t;

    // and so is this
    throw t;
}

此外,未指定返回或抛出的内容是returnthrow语句的表达式或该表达式的副本(或移动)的结果。

通过引用捕获的通常动机与生命无关 - 抛出对象的生命周期保证至少与catch子句一样长。它是首选,因为它允许异常设计和多态使用。

答案 1 :(得分:3)

C ++运行时使用独立于堆栈的内存位置来存储异常对象:

  

2.4.2分配异常对象

     

存在异常需要存储   抛出。此存储必须保留   堆栈正在解开,因为它   将由处理程序使用,并且必须   是线程安全的。异常对象   因此通常存储   虽然在堆中分配   实现可以提供   紧急缓冲以支持投掷   低内存下的bad_alloc例外   条件(见Section 3.3.1)。

(来自C++ ABI for Itanium: Exception Handling

因此,当您“通过引用捕获”时获得的引用是对该内存位置的引用,而不是对已释放的堆栈帧的引用。这意味着异常对象可以保证足够长,以供异常处理程序使用,即使通过引用获得也是如此。 (但是,一旦你离开捕获范围,它们可能会被释放,因此不要保留异常引用。)

答案 2 :(得分:2)

例外是本地范围规则的例外:

try
{
    Widget w;
    throw w;
}
catch (const Widget& exc)
{
    // exc is a valid reference to the Widget
}

即使本地范围已经结束,异常也会以特殊方式处理,因此抛出的内容仍然可以访问。

答案 3 :(得分:0)

在这一行中,您要创建本地对象的副本

throw localWidget;

因此,它不引用本地“localWidget”对象,而是引用该对象的副本(称为异常对象),该对象保证在catch子句完全处理异常之前存在

答案 4 :(得分:0)

抛出的实例在抛出时被复制。所以无论如何你总会得到一份副本。

最好通过引用捕获,因为抛出的对象可能是多态的,并且您不希望依赖于由多态类的副本生成的“错误代码”。 '错误代码'不会特定于抛出点抛出的派生类。