抛出异常

抛出异常

C++

编译器支持

自由(freestanding)与宿主(hosted)

语言

标准库

标准库头文件

具名要求

特性测试宏 (C++20)

语言支持库

概念库 (C++20)

诊断库

内存管理库

元编程库 (C++11)

通用工具库

容器库

迭代器库

范围库 (C++20)

算法库

字符串库

文本处理库

数值库

日期和时间库

输入/输出库

文件系统库 (C++17)

并发支持库 (C++11)

执行控制库 (C++26)

技术规范

符号索引

外部库

[编辑] C++ 语言

通用主题

预处理器

注释

关键词

转义序列

流程控制

条件执行语句

if

switch

迭代语句(循环)

for

range-for (C++11)

while

do-while

跳转语句

continue - break

goto - return

函数

函数声明

Lambda 函数表达式

inline 说明符

动态异常规范 (直到 C++17*)

noexcept 说明符 (C++11)

异常

throw 表达式

try 块

catch 处理程序

命名空间

命名空间声明

命名空间别名

类型

基本类型

枚举类型

函数类型

类/结构体类型

联合类型

说明符

const/volatile

decltype (C++11)

auto (C++11)

constexpr (C++11)

consteval (C++20)

constinit (C++20)

存储期说明符

初始化

默认初始化

值初始化

零初始化

复制初始化

直接初始化

聚合初始化

列表初始化 (C++11)

常量初始化

引用初始化

表达式

值类别

求值顺序

运算符

运算符优先级

替代表示

字面量

布尔 - 整型 - 浮点型

字符 - 字符串 - nullptr (C++11)

用户定义 (C++11)

工具

属性 (C++11)

类型

typedef 声明

类型别名声明 (C++11)

类型转换

隐式转换

static_cast

const_cast

显式转换

dynamic_cast

reinterpret_cast

内存分配

new 表达式

delete 表达式

类声明

构造函数

this 指针

访问说明符

friend 说明符

类特有的函数属性

虚函数

override 说明符 (C++11)

final 说明符 (C++11)

explicit (C++11)

static

特殊成员函数

默认构造函数

复制构造函数

移动构造函数 (C++11)

复制赋值

移动赋值 (C++11)

析构函数

模板

类模板

函数模板

模板特化

参数包 (C++11)

杂项

内联汇编

C++ 历史

[编辑] 表达式

通用

值类别

求值顺序

常量表达式

主表达式

Lambda 表达式 (C++11)

Requires 表达式 (C++20)

包索引表达式 (C++26)

潜在求值表达式

字面量

整数字面量

浮点数字面量

布尔字面量

字符字面量

转义序列

字符串字面量

空指针字面量 (C++11)

用户定义字面量 (C++11)

运算符

赋值运算符

递增和递减

算术运算符

逻辑运算符

比较运算符

成员访问运算符

其他运算符

new 表达式

delete 表达式

throw 表达式

alignof

sizeof

sizeof... (C++11)

typeid

noexcept (C++11)

折叠表达式 (C++17)

运算符的替代表示

优先级和结合性

运算符重载

默认比较 (C++20)

转换

隐式转换

显式转换

常用算术转换

用户定义转换

const_cast

static_cast

dynamic_cast

reinterpret_cast

[编辑] 异常

try 块

抛出异常

处理异常

异常规范

noexcept 规范 (C++11)

动态规范 (直到 C++17*)

noexcept 运算符 (C++11)

[编辑]

抛出异常会将控制权转移到处理程序。

异常可以从throw 表达式抛出,以下上下文也可能抛出异常

分配函数

dynamic_cast

typeid

new 表达式

标准库函数

目录

1 异常对象

1.1 构造和析构异常对象

2 throw 表达式

3 栈展开

4 注意

5 关键词

6 示例

7 缺陷报告

8 参考

9 参阅

[编辑] 异常对象

抛出异常会初始化一个具有动态存储期的对象,称为异常对象。

如果异常对象的类型是以下类型之一,则程序是病态的

不完整类型抽象类类型指向不完整类型的指针,除了(可能 cv 限定的)void

[编辑] 构造和析构异常对象

给定异常对象的类型为 T

令 obj 是类型为 const T 的左值,从 obj 复制初始化类型为 T 的对象必须是良构的。如果 T 是类类型

所选的构造函数是odr-used的。T 的析构函数是可能被调用的。

异常对象的内存以未指定的方式分配。唯一保证是存储永远不会由全局分配函数分配。

如果处理程序通过重新抛出退出,则控制权传递给同一异常对象的另一个处理程序。在这种情况下,异常对象不会被析构。

当异常的最后一个剩余活动处理程序以重新抛出以外的任何方式退出时,异常对象被销毁,并且实现可以以未指定的方式解除分配临时对象的内存。

销毁发生在处理程序中“参数列表”中声明的对象销毁之后。

(C++11 前)

异常对象的潜在销毁点是

当异常的活动处理程序以重新抛出以外的任何方式退出时,紧接在处理程序中“参数列表”中声明的对象(如果有)销毁之后。当引用异常对象的类型为std::exception_ptr的对象被销毁时,在std::exception_ptr的析构函数返回之前。

在异常对象的所有潜在销毁点中,有一个未指定的最后一个销毁点,异常对象在该点被销毁。所有其他点都发生在该最后一个点之前。然后,实现可以以未指定的方式解除分配异常对象的内存。

(C++11 起)

[编辑] throw 表达式

throw expression

(1)

throw

(2)

1) 抛出一个新异常。

2) 重新抛出当前正在处理的异常。

表达式

-

用于构造异常对象的表达式

当抛出新异常时,其异常对象确定如下

对 expression 执行数组到指针和函数到指针标准转换。令 ex 为转换结果

异常对象的类型通过从 ex 的类型中移除任何顶层 cv 限定符来确定。异常对象从 ex 复制初始化。

如果程序试图在没有异常正在处理时重新抛出异常,则将调用std::terminate。否则,异常会使用现有异常对象重新激活(不会创建新的异常对象),并且异常不再被认为是已捕获的。

try

{

// throwing a new exception 123

throw 123;

}

catch (...) // catch all exceptions

{

// respond (partially) to exception 123

throw; // pass the exception to some other handler

}

[编辑] 栈展开

异常对象构造完成后,控制流会向后(向上调用栈)工作,直到到达try 块的开头,此时将所有关联处理程序的参数与异常对象的类型按出现顺序进行比较,以找到匹配项。如果没有找到匹配项,控制流将继续展开栈,直到下一个try 块,依此类推。如果找到匹配项,控制流将跳转到匹配的处理程序。

随着控制流向上调用栈,所有具有自动存储期且在相应的try 块进入后已构造但尚未销毁的对象,其析构函数将按构造函数完成的逆序被调用。如果从局部变量的析构函数或在return语句中使用的临时变量的析构函数中抛出异常,则还会调用从函数返回的对象的析构函数。

如果从对象的构造函数或(罕见地)从析构函数中抛出异常(无论对象的存储期如何),则所有已完全构造的非静态非变体成员和基类的析构函数将按其构造函数完成的逆序被调用。类联合的变体成员只在构造函数展开的情况下被销毁,并且如果在初始化和销毁之间活动成员发生了变化,则行为是未定义的。

如果委托构造函数在非委托构造函数成功完成后以异常退出,则会调用此对象的析构函数。

(C++11 起)

如果异常是从new-expression调用的构造函数中抛出的,则如果可用,会调用匹配的解除分配函数。

此过程称为栈展开。

如果在异常对象初始化之后、异常处理程序开始之前,任何由栈展开机制直接调用的函数以异常退出,则会调用std::terminate。此类函数包括超出作用域的具有自动存储期对象的析构函数,以及(如果未省略)为初始化按值捕获的参数而调用的异常对象的复制构造函数。

如果抛出异常但未捕获,包括逃逸std::thread的初始函数、main函数以及任何静态或线程局部对象的构造函数或析构函数的异常,则会调用std::terminate。对于未捕获的异常是否进行任何栈展开是实现定义的。

[编辑] 注意

重新抛出异常时,必须使用第二种形式以避免在异常对象使用继承(典型情况)时发生对象切片

try

{

std::string("abc").substr(10); // throws std::out_of_range

}

catch (const std::exception& e)

{

std::cout << e.what() << '\n';

// throw e; // copy-initializes a new exception object of type std::exception

throw; // rethrows the exception object of type std::out_of_range

}

throw 表达式被分类为类型为void的prvalue 表达式。与任何其他表达式一样,它可能是另一个表达式中的子表达式,最常见的是在条件运算符中

double f(double d)

{

return d > 1e7 ? throw std::overflow_error("too big") : d;

}

int main()

{

try

{

std::cout << f(1e10) << '\n';

}

catch (const std::overflow_error& e)

{

std::cout << e.what() << '\n';

}

}

[编辑] 关键词

throw

[编辑] 示例

运行此代码

#include

#include

struct A

{

int n;

A(int n = 0): n(n) { std::cout << "A(" << n << ") constructed successfully\n"; }

~A() { std::cout << "A(" << n << ") destroyed\n"; }

};

int foo()

{

throw std::runtime_error("error");

}

struct B

{

A a1, a2, a3;

B() try : a1(1), a2(foo()), a3(3)

{

std::cout << "B constructed successfully\n";

}

catch(...)

{

std::cout << "B::B() exiting with exception\n";

}

~B() { std::cout << "B destroyed\n"; }

};

struct C : A, B

{

C() try

{

std::cout << "C::C() completed successfully\n";

}

catch(...)

{

std::cout << "C::C() exiting with exception\n";

}

~C() { std::cout << "C destroyed\n"; }

};

int main () try

{

// creates the A base subobject

// creates the a1 member of B

// fails to create the a2 member of B

// unwinding destroys the a1 member of B

// unwinding destroys the A base subobject

C c;

}

catch (const std::exception& e)

{

std::cout << "main() failed to create C with: " << e.what();

}

输出

A(0) constructed successfully

A(1) constructed successfully

A(1) destroyed

B::B() exiting with exception

A(0) destroyed

C::C() exiting with exception

main() failed to create C with: error

[编辑] 缺陷报告

下列更改行为的缺陷报告追溯地应用于以前出版的 C++ 标准。

缺陷报告

应用于

发布时的行为

正确的行为

CWG 499

C++98

未知边界的数组不能被抛出,因为其类型不完整,但异常对象可以从衰减的指针创建,没有任何问题

将类型完成要求应用于异常对象代替

CWG 668

C++98

如果从局部非自动对象的析构函数中抛出异常,则不调用std::terminate。从局部非自动对象的析构函数中

调用std::terminate在这种情况下

CWG 1863

C++11

当抛出仅可移动的异常对象时,不需要复制构造函数,但稍后允许复制抛出时,但允许稍后复制

要求复制构造函数

CWG 1866

C++98

从构造函数栈展开时,变体成员被泄露

变体成员已销毁

CWG 2176

C++98

从局部变量析构函数抛出可能会跳过返回值析构函数

函数返回值已添加到展开中

CWG 2699

C++98

throw "EX" 实际上会抛出 char* 而不是 const char*

已更正

CWG 2711

C++98

未指定异常对象的复制初始化源异常对象未指定

从 expression复制初始化

CWG 2775

C++98

异常对象复制初始化要求不明确

已明确

CWG 2854

C++98

异常对象的存储期不明确

已明确

P1825R0

C++11

禁止在throw中从参数隐式移动

允许

[编辑] 参考文献

C++23 标准 (ISO/IEC 14882:2024)

7.6.18 抛出异常 [expr.throw]

14.2 抛出异常 [except.throw]

C++20 标准 (ISO/IEC 14882:2020)

7.6.18 抛出异常 [expr.throw]

14.2 抛出异常 [except.throw]

C++17 标准 (ISO/IEC 14882:2017)

8.17 抛出异常 [expr.throw]

18.1 抛出异常 [except.throw]

C++14 标准 (ISO/IEC 14882:2014)

15.1 抛出异常 [except.throw]

C++11 标准 (ISO/IEC 14882:2011)

15.1 抛出异常 [except.throw]

C++03 标准 (ISO/IEC 14882:2003)

15.1 抛出异常 [except.throw]

C++98 标准 (ISO/IEC 14882:1998)

15.1 抛出异常 [except.throw]

[编辑] 另请参阅

复制省略

try 块

处理程序

noexcept 说明符

异常处理

动态异常规范

(C++17 前)

相关推荐

分期乐投诉去哪里?教你如何有效投诉分期乐
365bet足球真人

分期乐投诉去哪里?教你如何有效投诉分期乐

📅 08-21 👁️ 9461
科普了解一下代打骚扰电话,代打电话业务怎么收费
完美365体育ios下载

科普了解一下代打骚扰电话,代打电话业务怎么收费

📅 08-24 👁️ 9674
1纳秒等于多少秒
365bet足球真人

1纳秒等于多少秒

📅 09-17 👁️ 1419
用什么农药能杀死蝗虫幼虫_高效灭杀技术指南_科学用药省40%成本
慈世平为什么改名?慈世平和阿泰是一个人吗?
365bet赌城投注

慈世平为什么改名?慈世平和阿泰是一个人吗?

📅 12-15 👁️ 8572
德国国家足球队中英文双语介绍
365bet足球真人

德国国家足球队中英文双语介绍

📅 08-17 👁️ 7424