昨日总结
周末休息,晚上只看了约半个小时的《曾国藩传》,未完成周六的一道leetcode题目。
今日计划
继续学习多线程编程
完成一道leetcode题目。
学习内容
c++ 异常
异常处理机制为程序中异常检测和异常处理这两部分的协作提供支持。C++异常处理包括:
- throw表达式,异常检测部分使用throw表达式来表示她遇到了无法处理的问题。可以说throw引发了异常
- try语句块(try block)。异常处理部分使用了try语句块处理异常。try语句块以try开始,并以一个或者多个catch子句结束。try语句块中代码抛出的异常通常会被某个catch子句处理。catch子句“处理”异常,因此也将他们称作异常处理代码。
- 一套异常类,用于在throw表达式和相关catch子句之间传递异常的具体信息。
c++定义了一组标准异常类,用于报告标准库函数遇到的问题,这些异常类也可用于用户编写的程序中,他们分别包含在4个头文件中:
- exception头文件定义了最通用的异常类exception.它只报告异常的发生,不提供任何信息;
- stdexcept头文件定义了几种常见的异常类;
- new头文件中定义了bad_alloc异常类型;
- type_info头文件定义了bad_cast类型。
stdexcept文件中定义的异常
异常 | 意义 |
---|---|
exception | 最常见的问题 |
runtime_error | 只有在运行时才能检测出来的问题 |
range_error | 运行时错误:生成的结果超出了有意义的值域范围 |
overflow_error | 运行时错误:计算上溢 |
underflow_error | 运行时错误:计算下溢 |
logic_error | 程序逻辑错误 |
domain_error | 逻辑错误:参数对应的结果值不存在 |
invalid_argument | 逻辑错误:无效参数 |
length_error | 逻辑错误:试图创建一个超出类型最大长度的对象 |
out_of_range | 逻辑错误:使用一个超出有效范围的值 |
exception、bad_alloc和bac_cast对象不允许为这些对象提供初始值,只能默认初始化
其他异常类型则相反,应该使用string对象或者C风格字符串初始化,不允许使用默认初始化的方式。
所有异常类型都只定义了一个名为what的成员函数,无参,返回一个指向C风格字符串的const char*
测试代码:
#include <iostream>
#include <new>
#include <stdexcept>
using namespace std;
int exception_test(int i) {
if (i == 0) throw runtime_error("just test exception test");
if (i == 1) throw exception();
if (i == 2) throw bad_alloc();
if (i == 3) throw logic_error("logical error");
}
int main() {
for (int i = 0; i < 4; ++i) {
try {
exception_test(i);
} catch (runtime_error e) {
std::cout << e.what() << std::endl;
} catch (bad_alloc e) {
std::cout << e.what() << std::endl;
} catch (exception e) {
std::cout << e.what() << std::endl;
}
}
return 0;
}
just test exception test
std::exception
std::bad_alloc
std::exception
不能第一个catch exception ,否则后续异常捕获不到。如果将上述代码18与22行互换,则输出如下:
std::exception
std::exception
std::exception
std::exception
这里可以用exception来捕获任何标准异常,自己定义的异常如果继承自exception,则应该也可以,但具体不清楚。
这里catch的参数如果是基类型,则可以派生类进行初始化,如果为非引用类型,则派生类被切成基类,如上述所示,如果使用基类的引用,则类似于函数参数方式,参数以常规方式绑定到异常类中。因此上述代码可以简化为:
#include <iostream>
#include <new>
#include <stdexcept>
using namespace std;
int exception_test(int i) {
if (i == 0) throw runtime_error("just test exception test");
if (i == 1) throw exception();
if (i == 2) throw bad_alloc();
if (i == 3) throw logic_error("logical error");
}
int main() {
for (int i = 0; i < 4; ++i) {
try {
exception_test(i);
} catch (exception &e) { // 注意这里参数变成了引用
std::cout << e.what() << std::endl;
}
}
return 0;
}
输出:
just test exception test
std::exception
std::bad_alloc
logical error
异常重新抛出
有时,单独一个catch语句不能完整处理一个异常。在执行了一段校正操作后,当前的catch语句决定将由调用链更上一层的函数接着处理,该catch可以通过重新抛出(rethrowing)的操作将异常传递给另外一个catch。这里重新抛出仍然是一条throw语句,只不过不包含任何表达式:
throw;
空的throw语句只能出现在catch语句或者catch语句直接或间接调用的函数之内,如果在处理代码之外遇到了空throw语句,编译器将调用terminate结束整个程序。
捕获所有异常的处理代码\
catch(...)
上述语句可以捕获所有异常。通常与重新抛出语句一起使用。