通过引用捕获异常对象,临时变量,生命周期问题

5
今日免费次数已满, 请开通会员/明日再来
#include <iostream>
#include <stdexcept>

void foo()
{
    throw std::runtime_error("How long do I live?");
}

int main()
{
    try
    {
        foo();
    }
    catch (std::runtime_error& e)
    {
        std::cout << e.what() << std::endl;
    }
}

为什么可以通过引用捕获异常,std::runtime_error("How long do I live?")不是一个右值吗?

异常对象在catch块中如何仍然存活?

抛出的异常对象存储在哪里?它们的生命周期是什么?

4个回答

7
在C++标准第15.1.4段中提到:
被抛出的异常的临时复制品的内存是以未指定的方式分配的,除非在3.7.3.1中另有说明。临时变量会一直存在,只要正在执行该异常的处理程序。特别地,如果处理程序通过执行throw语句退出,则将控制传递给同一异常的另一个处理程序,因此临时对象仍然存在。当处理该异常的最后一个处理程序通过任何方式而不是throw语句退出时,临时对象将被销毁,实现可能会释放临时对象的内存;任何这样的释放都以未指定的方式进行。销毁发生在处理程序中声明的对象的销毁之后立即进行。
请注意,在C++标准术语中,处理程序表示具有正确参数类型的catch块。

粗体文本并不是指在throw表达式中创建的临时对象。重要的事实是,异常抛出机制会在某个私有区域中复制该临时对象,并保持该副本的存活状态。私有副本就是catch处理程序接收到的内容。 - bames53

5

抛出的异常不是临时的——编译器生成的异常代码会保留其永久副本。因此,您可以将其绑定到非const引用。

[编辑] 我刚刚查看了标准,实际上它是指一个临时副本。但是,临时对象的生命周期至少与异常处理程序一样长。


2
正如Neil所说,编译器内部有魔法正在发生。此外,请注意编译器允许创建异常对象的任意数量的副本

1

赞赏您尝试理解语言细节。同时,在我看来,理解为什么您应该通过引用捕获异常(并通过值抛出它),比理解为什么您可以这样做更加重要。

通常人们使用异常类的层次结构,通过引用捕获允许您利用多形性,并在没有必要单独处理异常类型时捕获基本类的异常。如果您不能通过引用捕获,那么您就需要为可能在try子句中抛出的每种可能的异常类型编写一个catch子句。


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