1. 简单介绍
异常是指在程序运行时发生的特殊情况,比如尝试除以零的操作。异常提供了一种转移程序控制权的方式。C++的异常处理设计三个关键字: try catch throw;
throw :当问题出现时,程序会抛出一个异常,是通过使用 throw 关键字完成;
try :try块中的代码标识将被激活的特定异常,后面通常跟着一个或多个catch块。
catch :在想要处理问题的地方,通过异常处理程序捕获异常,catch关键字用于捕获异常。
· try 块中的代码被称为保护代码,try块在不同的情境下会抛出不同的异常,可以尝试罗列多个catch语句,捕获不同类型的异常,如下:
try {
//保护代码
}catch(exceptionName e1) {
// 异常1
}catch(exceptionName e2) {
// 异常2
}
......
如果想处理try块抛出的任何类型的异常,catch语句的括号内使用省略号,如下:
try{
}catch(...){
}
· throw 语句在代码中的任何地方抛出异常,throw语句的操作数可以是任意的表达式,表达式的结果的类型决定抛出异常的类型;示例:
double division(int a,int b)
{
if(0 == b)
{
throw "Division by zero condition!";
}
return (a/b);
}
int main()
{
int x=50,y=0;
double z = 0;
try {
z = division(x,y);
cout << z <<endl;
}catch(const char* msg) {
cerr << msg << endl;
}
return 0;
}
执行后的结果:Division by zero condition!
2. 标准异常
C++定义了一系列标准的异常,定义在<exception>中,有如下异常:
- std::exception 是所有标准C++异常的父类
- std::bad_alloc 该异常可以通过new抛出
- std::bad_cast 该异常通过dynamic_cast抛出,使用dynamic_cast进行从多态基类对象到派生类的引用的强制类型转换时,如果转换是不安全的,会抛出此异常。
- std::bad_exception 处理C++程序中无法预期的异常非常有用;
- std::bad_typeid 通过typeid抛出,如果操作数是一个多态类的指针,该指针的值为null,会抛出异常
- std::logic_error 理论上可以通过读取代码来检测到异常
- std::domain_error 当使用了一个无效的数学域时,会抛出该异常。
- std::invalid_argument 当使用了无效的参数时,会抛出该异常。
- std::length_error 创建了太长的std::string时,会抛出该异常。
- std::out_of_range 该异常可以通过方法抛出,用vector或string的at成员函数根据下标访问元素时,如果下标越界,则会抛出此异常。使用operate[]不会抛出异常。
- std::runtime_error 理论上不可以通过代码来检测到的异常;
- std::overflow_error 发生数学上溢时,会抛出该异常;
- std::range_error 尝试存储超出范围的值,抛出该异常;
- std::underflow_error 发生数学下溢时,会抛出该异常。
3. 异常处理时需要注意的问题
序号 | 需要注意的问题 |
1 | 如果抛出的异常一直没有被函数捕获,会一直上传到C++运行系统那里,导致整个程序的终止;所以需要处理所以抛出的异常 |
2 |
一般在异常抛出后资源可以正常被释放,如果在类的构造函数中抛出异常,系统不会调用析构函数; 处理方法:如果构造函数中要抛出异常,在抛出前要记得删除申请的资源 |
3 | 异常处理仅仅通过类型去匹配,catch参数可以没有参数名称,只需要参数类型 |
4 | 函数原型中的异常说明要与实现中的异常说明一致,否则容易引起异常冲突 |
5 |
应该在throw语句后写上异常对象,throw先通过copy构造函数构造一个新对象,再把该新对象传递给catch. 异常处理机制保证:异常抛出的新对象并非创建在函数栈上,而是创建在专用的异常栈上,因此它才可以跨接多个函数而传递到上层,否则在栈清空的过程中就会被销毁。 所有从try到throw语句之间构造起来的对象的析构函数将被自动调用。如果一直上溯搭配main函数后还没有找到匹配的catch块,那么系统将调用terminate()终止整个程序,这种情况下不能保证所有的局部对象会被正确的销毁。 |
6 | catch块的参数推荐采用地址传递而不是值传递,不仅可以提高效率,还可以利用对象的多态性。另外,派生类的异常捕获要放在父类异常捕获的前面,否则,派生类的异常无法被捕获; |
7 | 编写异常说明时,要确保派生类成员函数的异常说明和基类成员函数的异常说明一致,即派生类改写的虚函数的异常说明至少要和对应的基类虚函数的异常说明相同,甚至更加严格,更特殊, |
4. 定义新的异常
通过继承和重载exception类定义新的异常
what()是异常类提供的一个公共方法,被所有子异常类重载,将返回异常产生的原因。
5. 函数的异常声明列表
可以在函数声明时写,也可以在函数定义时写,如果两处都写,则两处应一致。
// 比如
void func() throw (int,double,A,B,C,...);
// 或者
void func() throw (int ,double,A,B,C,...){}
如果异常声明列表如下编写,不会抛出任何异常
void func() throw();//函数不会抛出任何异常;
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/46198.html