异常是程序在执行期间产生的问题,不是在编译期的问题,例如之前遇见的字符串下标越界问题。异常提供了一种转移程序控制权的方式,此时异常未经过正确的处理就会造成程序运行的崩溃。
处理异常的方式主要有两种:
● 捕获异常(try-catch)
● 抛出异常(throw)
C++的异常处理机制并不完善,是否使用取决于开发团队。
1.1 抛出异常
当程序咋运行期间出现问题时,C++运行系统会创建一个异常对象,此对象携带了一些本次出现异常的信息,并且抛给程序执行的函数。
#include <iostream>
using namespace std;
double divide(int a,int b)
{
if(b==0)
{
throw "除数为零!";
return -1;
}
return a/b;
}
int main()
{
cout << "开始" << endl;
string s;
// cout << s.at(-1); C++底层抛出给主函数的异常
divide(1,0);
cout << "终止" << endl;
return 0;
}
1.2 捕获异常
如果某段代码可能有异常对象的抛出,可以使用try-catch代码块来捕获异常。
● try代码块
尝试执行某段可能出错的代码块,try块的代码尽量少。一个try块只能处理第一次出现的异常对象。
● catch代码块
如果try块出现了异常,catch块用固定的类型的异常处理代码来匹配try块中出现的异常类型,存在两种情况:
1. 匹配成功:从try块抛出异常处直接跳转到catch块。
2. 匹配失败:try-catch失效
C++中预定义了一些异常类型,使用时需要引入头文件<stdexcept>,如下所示:
1.2.1 最简单的单重捕获
#include <iostream>
// 头文件
#include <stdexcept>
using namespace std;
double divide(int a,int b)
{
if(b==0)
{
throw "除数为零!";
return -1;
}
return a/b;
}
int main()
{
cout << "开始" << endl;
try
{
string s;
cout << s.at(-1) << endl;
}catch(out_of_range &e)
{
// 输出异常类型
cout << e.what() << endl;
// 补救措施
cout << 'A' << endl;
}
try
{
cout << divide(1,0) << endl;
}catch(const char* e)
{
// 输出异常类型
cout << e << endl;
// 补救措施
cout << "无穷" << endl;
}
cout << "终止" << endl;
return 0;
}
1.2.2 不精准的捕获
有时候无法精准地判断异常出现的固定的类型,此时可以使用异常的基类进行捕获,这种的优势是只要捕获的类型是抛出的类型的基类即可成功,劣势是无法精准匹配,补救措施的效率可能会降低。
#include <iostream>
// 头文件
#include <stdexcept>
using namespace std;
int main()
{
cout << "开始" << endl;
try
{
string s;
cout << s.at(-1) << endl;
}catch(out_of_range &e) // out_of_range → logic_error → exception(...)
{
cout << e.what() << endl;
cout << 'A' << endl;
}
cout << "终止" << endl;
return 0;
}
1.2.3 多重捕获
一个try块同时配合多个catch块进行多重异常类型的捕获,多重捕获支持派生类异常与基类异常同时使用,但是基类异常要放在派生类异常的后面。
#include <iostream>
// 头文件
#include <stdexcept>
using namespace std;
int main()
{
cout << "开始" << endl;
try
{
string s;
cout << s.at(-1) << endl;
}catch(length_error &e)
{
cout << e.what() << endl;
cout << 'A' << endl;
}catch(out_of_range &e)
{
cout << e.what() << endl;
cout << 'B' << endl;
}catch(logic_error &e)
{
cout << e.what() << endl;
cout << 'C' << endl;
}catch(exception &e)
{
cout << e.what() << endl;
cout << 'D' << endl;
}
cout << "终止" << endl;
return 0;
}
1.3 自定义异常
C++标准异常类型较少,如果要使用异常处理机制,还需要自己定义不同的异常类型。
#include <iostream>
#include <stdexcept>
using namespace std;
class DividedException:public exception
{
public:
const char* what() const throw()
//异常规格说明,表示what()可以抛出异常的类型,()为空。表示what本身不抛出异常。
{
return "除数为0!";
}
};
double divide(int a,int b)
{
if(b==0)
{
throw DividedException();
return -1;
}
return a/b;
}
int main()
{
cout << "开始" << endl;
try
{
cout << divide(1,0) << endl;
}catch(DividedException &e)
{
cout << e.what() << endl;
cout << -1 << endl;
}
cout << "终止" << endl;
return 0;
}
1.4 栈自旋
#include <iostream>
using namespace std;
class A
{
public:
A()
{
cout<<"A constructor"<<endl;
}
~A()
{
cout<<"~A destructor"<<endl;
}
};
int divide(int x, int y)
{
A a;
if(y == 0)
throw('a');
return x/y;
}
int myDivide(int x, int y)
{
A b;
divide(x,y) ;
}
int main()
{
try{
myDivide(4,0);
}catch(int x){
cout<<"x"<<endl;
cout<<x<<endl;
}catch(double y){
cout<<"y"<<endl;
cout<<y<<endl;
}catch(...){
cout<<"no x, no y"<<endl;
}
return 0;
}