目录
1. 异常的概念
2. throw 抛出异常
3. try-catch 捕获异常
4. 标准异常
5. 捕获基类异常
6. 多重捕获
1. 异常的概念
异常是程序在运行期间产生的问题,而非编译期间产生的问题,因此程序的语法没有问题,但是逻辑出现了问题。
异常本质上提供的是一种程序控制权转移的方式,程序一旦出现异常没有经过正确的处理,就会造成程序运行的崩溃。
处理异常的方式有两种:
- 抛出异常 throw
- 捕获异常 try-catch
2. throw 抛出异常
throw语句抛出的是通常是一个对象,对象的类型就是异常的类型,throw可以把这个对象抛出到调用的上一级。如果上一级没有正确的处理异常,异常对象会逐级向上反馈,如果在主函数中仍然没有正确处理异常,程序运行崩溃。
#include <iostream>using namespace std;/*** @brief divide 除法* @param a 被除数* @param b 除数* @return 商*/
double divide(double a,double b)
{if(b == 0)throw "除数为0!!!";return a/b;
}int main()
{cout << divide(4,5) << endl;cout << divide(6,7) << endl;cout << divide(1,0) << endl; // 运行终止cout << "主函数结束" << endl;return 0;
}
3. try-catch 捕获异常
在try块中放置可能抛出异常的代码,在catch代码中对异常类型进行匹配:
- 如果匹配成功,程序从try块抛出异常的位置直接跳转到catch块,执行弥补错误的代码。执行完catch块后,try-catch结束。
#include <iostream>using namespace std;/*** @brief divide 除法* @param a 被除数* @param b 除数* @return 商*/
double divide(double a,double b)
{if(b == 0)throw "除数为0!!!";return a/b;
}int main()
{cout << divide(4,5) << endl;try{cout << divide(1,0) << endl;cout << divide(6,7) << endl; // 没有执行的机会}catch(const char* e){cout << e << endl;// 弥补的措施}cout << "主函数结束" << endl;return 0;
}
- 如果匹配失败,异常会交给上一级处理,直到主函数。
#include <iostream>using namespace std;/*** @brief divide 除法* @param a 被除数* @param b 除数* @return 商*/
double divide(double a,double b)
{if(b == 0)throw "除数为0!!!";return a/b;
}void test()
{try{cout << divide(1,0) << endl;cout << divide(6,7) << endl; // 没有执行的机会}catch(string e) // 匹配失败{cout << e << endl;// 弥补的措施}
}int main()
{cout << divide(4,5) << endl;try{test();}catch(const char* e) // 最后一次机会捕获成功{cout << e << endl;// 弥补措施}cout << "主函数结束" << endl;return 0;
}
4. 标准异常
C++为常见的异常类型进行了分类,如下所示。
使用上面的异常类型需要引入头文件 #include <stdexcept>
程序员自定义异常最好通过继承加入到上面的标准异常家族中,方便后续处理和管理。
#include <iostream>
#include <stdexcept> // 头文件using namespace std;// 自定义异常类型
class ZeroException:public exception
{
public:// throw() 是异常规格说明,表示此函数不会抛出异常const char* what() const throw(){return "除数为0!!!";}
};/*** @brief divide 除法* @param a 被除数* @param b 除数* @return 商*/
double divide(double a,double b)
{if(b == 0)throw ZeroException();return a/b;
}int main()
{cout << divide(4,5) << endl;try{cout << divide(1,0) << endl;cout << divide(6,7) << endl; // 没有执行的机会}catch(const ZeroException& e){cout << e.what() << endl;// 弥补的措施}cout << "主函数结束" << endl;return 0;
}
5. 捕获基类异常
可以捕获基类异常,同时匹配所有派生类异常类型。
#include <iostream>
#include <stdexcept> // 头文件using namespace std;class Animal
{
public:string a = "Animal";virtual void eat(){cout << "吃东西" << endl;}
};class Monkey:public Animal
{
public:string b = "Monkey";virtual void eat(){cout << "吃香蕉" << endl;}
};int main()
{string s = "fsdjkfgsdhj";int err_id;cin >> err_id;try{if(err_id%3 == 0) // 可以捕获cout << s.at(348568734) << endl;else if(err_id%3 == 2) // 可以捕获throw length_error("长度错误");else // 不能捕获{Animal a1;Monkey& m2 = dynamic_cast<Monkey&>(a1);}}catch(logic_error& e){cout << e.what() << endl;cout << "弥补措施" << endl;}cout << "主函数结束" << endl;return 0;
}
6. 多重捕获
在上面的代码中,虽然可以同时处理多种异常类型,但是是牺牲捕获精度实现的,为了保证捕获的精度,可以使用多重捕获。
在使用多重捕获时,一定要先catch派生类异常类型,再catch基类异常类型。
#include <iostream>
#include <stdexcept> // 头文件using namespace std;class Animal
{
public:string a = "Animal";virtual void eat(){cout << "吃东西" << endl;}
};class Monkey:public Animal
{
public:string b = "Monkey";virtual void eat(){cout << "吃香蕉" << endl;}
};int main()
{string s = "fsdjkfgsdhj";int err_id;cin >> err_id;try{if(err_id%3 == 0) // 可以捕获cout << s.at(348568734) << endl;else if(err_id%3 == 2) // 可以捕获throw length_error("长度错误");else // 不能捕获{Animal a1;Monkey& m2 = dynamic_cast<Monkey&>(a1);}}catch(out_of_range& e){cout << e.what() << endl;cout << "分支1" << endl;}catch(length_error& e){cout << e.what() << endl;cout << "分支2" << endl;}catch(logic_error& e){cout << e.what() << endl;cout << "保底1" << endl;}catch(exception& e){cout << e.what() << endl;cout << "保底2" << endl;}cout << "主函数结束" << endl;return 0;
}
C++的异常处理机制并不完善,是否使用取决于所在的开发团队和技术框架。