可能重复:
C ++中异常对象的范围
我有以下catch子句:
catch(Widget w);
catch(Widget& w);
void passAndThrowWidget() {
Widget localWidget;
throw localWidget;
}
如果我们按值捕获Widget对象,编译器将进行复制,因此当我们抛出异常时,localWidget超出范围,我们没有看到任何问题。
如果我们通过引用捕获小部件对象,根据参考概念,“w”指向相同的本地Widget而不是副本。但我已经看到大多数异常都是由C ++中的引用捕获的。我的问题是如何工作“localWidget”超出范围时抛出异常并通过referense指向被破坏的对象。
谢谢!
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;
}
此外,未指定返回或抛出的内容是否为表达式的结果 return
要么 throw
语句,或该表达式的副本(或移动)。
通过引用捕获的通常动机与生命无关 - 抛出对象的生命周期保证至少与catch子句一样长。它是优选的,因为它允许以多态方式设计和使用异常。
C ++运行时使用独立于堆栈的内存位置来存储异常对象:
2.4.2分配异常对象
例外情况需要存储
抛出。此存储必须保留
堆栈正在解开,因为它
将由处理程序使用,并且必须
是线程安全的。异常对象
因此通常存储
虽然在堆中分配
实现可以提供
紧急缓冲,以支持投掷
bad_alloc
低内存下的异常
条件(见 第3.3.1节)。
(来自 用于Itanium的C ++ ABI:异常处理)
因此,当您“通过引用捕获”时获得的引用是对该内存位置的引用,而不是对已释放的堆栈帧的引用。这意味着异常对象可以保证足够长,以供异常处理程序使用,即使通过引用获取也是如此。 (但是,一旦你离开捕获范围,它们可能会被释放,因此不要保留异常引用。)
例外是本地范围规则的例外:
try
{
Widget w;
throw w;
}
catch (const Widget& exc)
{
// exc is a valid reference to the Widget
}
即使本地范围已经结束,异常也会以特殊方式处理,因此抛出的内容仍然可以访问。
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;
}
此外,未指定返回或抛出的内容是否为表达式的结果 return
要么 throw
语句,或该表达式的副本(或移动)。
通过引用捕获的通常动机与生命无关 - 抛出对象的生命周期保证至少与catch子句一样长。它是优选的,因为它允许以多态方式设计和使用异常。
C ++运行时使用独立于堆栈的内存位置来存储异常对象:
2.4.2分配异常对象
例外情况需要存储
抛出。此存储必须保留
堆栈正在解开,因为它
将由处理程序使用,并且必须
是线程安全的。异常对象
因此通常存储
虽然在堆中分配
实现可以提供
紧急缓冲,以支持投掷
bad_alloc
低内存下的异常
条件(见 第3.3.1节)。
(来自 用于Itanium的C ++ ABI:异常处理)
因此,当您“通过引用捕获”时获得的引用是对该内存位置的引用,而不是对已释放的堆栈帧的引用。这意味着异常对象可以保证足够长,以供异常处理程序使用,即使通过引用获取也是如此。 (但是,一旦你离开捕获范围,它们可能会被释放,因此不要保留异常引用。)
例外是本地范围规则的例外:
try
{
Widget w;
throw w;
}
catch (const Widget& exc)
{
// exc is a valid reference to the Widget
}
即使本地范围已经结束,异常也会以特殊方式处理,因此抛出的内容仍然可以访问。
在这一行中,您将创建本地对象的副本,
throw localWidget;
因此,它不会引用您的本地“localWidget”对象,而是引用该对象的副本(称为 异常对象保证在catch子句完全处理异常之前一直存在。
抛出的实例在抛出时被复制。所以无论如何你总会得到一份副本。
最好通过引用捕获,因为抛出的对象可能是多态的,并且您不希望依赖于由多态类的副本生成的“错误代码”。 '错误代码'不是特定于在抛出点抛出的派生类。