异常是指存在于运行时的反常行为,这些行为超出了函数正常功能的范围。典型的异常包括失去数据库连接以及遇到意外输入等。目前觉得相对适用的就是what、when、why和how,下面是在笔记中记录的一段话,,它从语言设计的层面考虑了 C++中为什么添加异常,觉得很棒,分享给大家:
接下来,来了解 C++的异常处理机制。
C++异常处理机制通过异常检测和异常处理两部分进行支持,包括:
-
throw
表达式,异常检测部分使用throw
表达式抛出异常。 -
try
语句块,异常处理部分采用try
语句块进行异常处理,try
语句块以关键字try
开始,并以一个或多个catch
子句结束。try
语句块中代码抛出的异常通常会被某个catch
子句处理。因为catch
子句“处理”异常,所以它们也被称作异常处理代码(exception handler)。 -
一套异常类,用于在
throw
表达式和相关的catch
子句之间传递异常的具体信息,C++中提供了标准exception
库
简单的异常处理
在 C++标准库定义了一组类,用于报告标准库函数遇到的问题。这些异常类也可以在用户编写的程序中使用,它们分别定义在 4 个头文件中:
-
exception
头文件定义了最通用的异常类 exception。它只报告异常的发生,不提供任何额外信息。 -
stdexcept
头文件定义了几种常用的异常类
-
new
头文件定义了bad_alloc
异常类型,默认,如果new
不能分配所要求的内存空间,它会抛出一个类型为bad_alloc
的异常。我们可以改变使用 new 的方式来阻止它抛出异常:
// 如果分配失败,new抛出std::bad_alloc
int *p1 = new int;
// 如果分配失败,new返回一个空指针
int *p2 = new (nothrow) int;
type_info 头文件定义了 bad_cast 异常类型,dynamic_cast<type&>(e)如果转换目标是引用类型并且失败了,则dynamic_cast运算符将抛出一个bad_cast异常。
#include <iostream>
#include <exception>
void tryException() {
// 此处是模拟处理出现问题,抛出异常
throw std::runtime_error("This's just exception test!");
}
int main(int argc, char* argv[]) {
try {
tryException();
} catch(const std::runtime_error& e) {
std::cout << "catch runtime_error for " << e.what() << std::endl;
}
}
函数在寻找处理代码的过程中退出
LinuxC++服务器开发视频:c/c++ linux服务器开发/后台架构师-点击观看
需要C/C++ Linux服务器架构师学习资料加裙994289133获取(资料包括C/C++,Linux,golang技术,Nginx,ZeroMQ,MySQL,Redis,fastdfs,MongoDB,ZK,流媒体,CDN,P2P,K8S,Docker,TCP/IP,协程,DPDK,ffmpeg等),免费分享。
异常规格
异常规格对函数定义进行限定,指出其可能引发那些类型的异常,同时提醒程序员此处可能抛出异常应该使用try-catch
和处理程序,其由 throw 关键字和异常类型列表组成,异常类型括在括号中,用逗号分割。函数的异常规格不仅要包含自身触发的异常类型,还要调用其他函数可能触发的异常类型。异常规则在 C++11 中被舍弃,在 C++17 中被彻底移除,改用noexcept(true)
和noexcept(false)
,前者表示该实现无异常抛出,后者表示所有的异常。
// 可能抛出任何异常
void func();
// 只能抛出char*和int类型的异常
void func() throw(const char*, int);
// 不抛出任何异常
void func() throw();
意外异常
如果带异常规格的函数抛出了和规格列表中不匹配的异常,称之为意外异常,会调std::unexpected
,该函数默认会调用std::terminate
,用户也可以通过std::set_unexpected
设定用户自己设定的行为
typedef void (*unexpected_handler)();
unexpected_handler set_unexpected(unexpected_handler f) throw();
void unexpected();
unexpected_handler
函数行为遵守严格的限制:
-
通过调用
terminate
(默认)、abort()
、exit()
来终止程序。 -
引发异常,结果取决于
unexpected_handler
函数所引发的异常以及引发意外异常的函数异常规范: -
新引发的异常和原异常规范匹配,则程序寻找与新引发异常匹配的
catch
块,该行为相当于用预期异常取代意外异常。 -
新引发的异常和原异常规范不匹配且异常规范没有
std::bad_exception
类型,则程序将调用terminate()
。 -
新引发的异常和原异常规范不匹配且异常规范包含
std::bad_exception
类型,则不匹配的异常将被std::bad_exception
异常所取代
#include <iostream>
#include <exception>
void myUnexpected() {
std::cout << "catch unexpected exception!" << std::endl;
// 1. 将意外异常统一转化为bad_exception,或者其他的异常
// throw std::bad_exception();
// 2. 抛出和异常规格匹配的异常,程序寻找匹配的catch处理块
throw "----------- satify exception specification --------";
}
// 异常规格,该接口支持抛出const char*的异常
// void handleMessage() throw(const char*, std::bad_exception){ // for understanding
void handleMessage() throw(const char*) {
std::cout << __func__ << std::endl;
throw std::underflow_error("error::underflow");
}
int main(int argc, char* argv[]) {
try {
std::set_unexpected(myUnexpected);
handleMessage();
} catch (const char* e) {
std::cout << e << std::endl;
} catch (std::bad_exception& e) {
std::cout << "catch bad_exception: " << e.what() << std::endl;
}
return 0;
}
未捕获异常
没有try
块或者catch
块时,异常未捕获,称之为未捕获异常。默认,将会导致程序异常终止。也可以修改程序对意外异常和未捕获异常的反应。未捕获的异常,通常不会立刻导致程序终止,程序首先会调用terminate()
此时会调用std::abort()
函数,我们可以修改terminate()
的实现来实现行为定制。相关接口定义:
typedef void (*terminate_handler)();
terminate_handler set_terminate(terminate_handler f) throw();
void terminate();
假设程序抛出了一个未捕获的运行时异常,并自定义终止terminate_handler
。
#include <iostream>
#include <exception>
void myTerminateHandler() {
std::cout << "Error catch uncatched exception!" << std::endl;
exit(-1);
}
int main(int argc, char* argv[]) {
// 自定义terminate_handler行为
std::set_terminate(myTerminateHandler);
// 抛出一个未捕获的异常,将会触发terminate_handler
throw std::runtime_error("This's just a test");
return 0;
}