【问题标题】:C++ exceptions catch clause [duplicate]C ++异常捕获子句[重复]
【发布时间】:2011-07-06 04:55:15
【问题描述】:

可能重复:
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”超出范围时,它是如何工作的。

谢谢!

【问题讨论】:

    标签: c++


    【解决方案1】:

    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 子句一样长。它是首选,因为它允许以多态方式设计和使用异常。

    【讨论】:

      【解决方案2】:

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

      2.4.2 分配异常对象

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

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

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

      【讨论】:

        【解决方案3】:

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

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

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

        【讨论】:

        • +1,如果是真的那么一个合理的答案。
        • return w; 也是例外吗?
        • @Luc Danton,不在 C++ 中。异常有一个复杂的 API 来支持它们并强制堆栈展开。 return 语句只是将数据返回到前一帧,要么将其放入寄存器中,要么放入调用者已知位置的堆栈中;异常以这样一种方式传播,调用者甚至可能完全不知道它们。但是,还有其他语言的返回值是异常(或者异常是返回值,以您的船漂浮为准)。
        • @Luc,哦,你的评论突然变得更有意义了。我要睡觉了。
        • @Luc:不,如果你通过引用返回本地,你会得到一个悬空引用。返回值和抛出的异常在生命周期方面非常不同。
        【解决方案4】:

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

        throw localWidget;
        

        所以,它不是指您本地的“localWidget”对象,而是该对象的副本(称为异常对象),它保证在异常被 catch 子句完全处理之前一直存在.

        【讨论】:

          【解决方案5】:

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

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

          【讨论】:

            猜你喜欢
            • 2016-05-28
            • 1970-01-01
            • 2014-01-25
            • 2018-11-29
            • 2012-07-13
            • 2011-12-07
            • 2012-09-22
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多