一、异常的概念
异常是一种处理错误的方式,当一个函数出现无法处理的错误时,可以抛出异常,函数的直接调用或者间接调用者捕获并处理错误.
throw关键字可以用来抛出异常
使用try catch组合捕获异常,try块儿为可能会抛出异常的代码块,catch为异常处理模块.
try
{
// 保护的标识代码
}catch( ExceptionName e1 )
{
// catch 块
}catch( ExceptionName e2 )
{
// catch 块
}catch( ExceptionName eN )
{
// catch 块
}
二、异常的使用
1.异常是根据抛出的对象引发的,激活那一个catch处理代码是根据抛出的对象类型确定的.
#include<iostream>
using namespace std;
double Division(int a, int b)
{
// 当b == 0时抛出异常
if (b == 0)
throw "Division by zero condition!";//抛出的对象类型是const char*
else
return ((double)a / (double)b);
}
void Func()
{
int len, time;
cin >> len >> time;
//1.func是异常模块儿的直接调用者
try
{
cout << Division(len, time) << endl;
}
catch (char str)//捕获的异常对象类型是char
{
cout << str << endl;
}
cout << "void Func()" << endl;
}
int main()
{
//1.main是异常模块儿的间接调用者
try
{
Func();
}
catch (const char* str)//捕获异常对象的类型是const char*
{
cout << str << endl;
}
return 0;
}
2.catch捕获对象类型相同,优先激活调用距离被调用异常函数最近的那一个catch代码块儿
#include<iostream>
using namespace std;
double Division(int a, int b)
{
// 当b == 0时抛出异常
if (b == 0)
throw "Division by zero condition!";
else
return ((double)a / (double)b);
}
void Func()
{
int len, time;
cin >> len >> time;
//1.func是异常模块儿的直接调用者
try
{
cout << Division(len, time) << endl;
}
catch (const char* str)
{
cout << str <<"func" << endl;
}
cout << "void Func()" << endl;
}
int main()
{
//1.main是异常模块儿的间接调用者
try
{
Func();
}
catch (const char*str)
{
cout << str<<"main" << endl;
}
return 0;
}
3,throw抛出异常对象后会生成一个临时对象,这里临时对象会在catch以后销毁,这是因为throw抛出的可能会是一个临时对象,处理作用域就销毁了,而throw往往跟catch不在一个作用域.这类似于传值返回,所以catch是不能用引用来做形参的.
4.catch(…)可以捕获任意类型的异常,问题是不知道异常错误是什么,通常用一个异常类封装错误代码和错误信息,抛出异常的时候抛出类对象,捕获异常打印异常信息只需接输出对象成员变量即可.
#include<iostream>
using namespace std;
class Exception
{
public:
Exception(int errid, const string& err_message)
:_errid(errid),_err_message(err_message){}
const int& getErrid()const { return _errid; }
const string& getErrMessage()const{ return _err_message; }
protected:
int _errid;
string _err_message;
};
double Division(int a, int b)
{
// 当b == 0时抛出异常
if (b == 0)throw Exception(1, "除0错误");
else return ((double)a / (double)b);
}
int main()
{
try{Division(1, 0);}
catch (const Exception err){cout << err.getErrMessage()<<"main" << endl;}
return 0;
}
5.在函数调用链中,异常匹配原则是查看throw本身是否直接在try代码块儿中,如果在查找对应的catch代码块儿处理错误,如果不在则跳出当前的函数栈,到调用该函数的函数栈中查找匹配的catch,如此重复上述动作,如果一直到main都没有匹配的catch代码块,则程序退出.如果有匹配的catch,catch执行完之后会执行catch后面的代码.
6.异常有可能因为一些原因没有被捕获到,造成程序退出,影响其他代码块儿的运行,可以使用任意类型异常捕获,一般将捕获任意类型的catch放在最后防止意外.
#include<iostream>
using namespace std;
class Exception
{
public:
Exception(int errid, const string& err_message)
:_errid(errid),_err_message(err_message){}
const int& getErrid()const { return _errid; }
const string& getErrMessage()const{ return _err_message; }
protected:
int _errid;
string _err_message;
};
double Division(int a, int b)
{
// 当b == 0时抛出异常
if (b == 0)
throw Exception(1,"除0异常");
else
return ((double)a / (double)b);
}
int sub(int a, int b)
{
throw 1;//未知异常
return a + b;
}
int main()
{
//sub抛出异常不会再执行Division
try { cout << sub(1, 3) << endl; Division(1, 0);}
catch (const Exception& e) {cout << e.getErrMessage() << endl; }
// 放到最后,捕获任意类型的异常,防止有一些异常没捕获,导致程序终止
catch (...) { cout << "未知异常" << endl; }
cout << "end" << endl;
return 0;
}
7.有时候try catch无法一次性处理异常,或者因为抛出异常所造成的其他问题,所以catch处理完异常的一部分问题可以直接使用throw将异常重新抛出
#include<iostream>
using namespace std;
double Division(int a, int b)
{
// 当b == 0时抛出异常
if (b == 0){throw "Division by zero condition!";}
return (double)a / (double)b;
}
void Func()
{
int* array = new int[10];
//捕获Division的异常,如果成功则将直接跳转到main函数,会造成array的内存泄漏
try {cout << Division(0,0) << endl;}
//先阶段捕获异常处理内存泄漏问题,在将异常重新抛出.
catch (...){delete[] array;throw;}
cout << "delete []" << array << endl;
delete[] array;
}
int main()
{
try{Func();}
catch (const char* errmsg){cout << errmsg << endl;}
return 0;
}
8.实际中抛出和捕获的匹配原则有个例外,并不都是类型完全匹配,可以抛出的派生类对象,使用基类捕获,抛出和捕获的对象之间构成多态,这样可以知道错误处在哪里,也规范了抛出异常的类型,实际应用过程中,通公司都会自定义异常体系结构.
#include<iostream>
#include<thread>
using namespace std;
//下面是服务器常用异常体系
//基类异常
class Exception
{
public:
Exception(int errid, const string& msg)
:_errid(errid), _errmsg(msg){}
virtual string what() const{return _errmsg;}
int GetErrid() const{return _errid;}
protected:
int _errid; // 错误码
string _errmsg; // 错误描述
};
//数据库异常
class SqlException : public Exception
{
public:
SqlException(int errid, const string& msg, const string& sql)
:Exception(errid, msg), _sql(sql){}
//重写基类的错误信息获取函数
virtual string what() const{return string("SqlException:" +_errmsg+"->"+_sql);}
protected:
string _sql;
};
class CacheException : public Exception
{
public:
CacheException(const string& errmsg, int id)
:Exception(id, errmsg){}
//重写基类的错误信息获取函数
virtual string what() const{return string ("CacheException:"+_errmsg);}
};
class HttpServerException : public Exception
{
public:
HttpServerException(const string& errmsg, int id, const string& type)
:Exception(id, errmsg), _type(type){}//继承构造调用基类的构造函数构造基类的成员
//重写基类的错误信息获取函数
virtual string what() const{return string("HttpServerException:" + _errmsg+"->"+_type);}
private:
const string _type;
};
void SQLMgr()
{
srand(time(0));//抛出派生列
if (rand() % 7 == 0){throw SqlException(100, "权限不足", "select * from name = '张三'");}
cout << "调用成功" << endl;
}
void CacheMgr()
{
srand(time(0));//抛出派生列
if (rand() % 5 == 0){throw CacheException("权限不足", 100);}
else if (rand() % 6 == 0){throw CacheException("数据不存在", 101);}
SQLMgr();
}
void HttpServer()
{
// 模拟服务出错
srand(time(0));//抛出派生列
if (rand() % 3 == 0){throw HttpServerException("请求资源不存在", 100, "get");}
else if (rand() % 4 == 0){throw HttpServerException("权限不足", 101, "post");}
CacheMgr();
}
int main()
{
while (1)
{
this_thread::sleep_for(chrono::seconds(1));
try{HttpServer();}
catch (const Exception& e){cout << e.what() << endl;}//捕获父类
catch (...){cout << "Unkown Exception" << endl;}
}
return 0;
}
9.c++标准库的异常体系
10.异常规范
// 这里表示这个函数会抛出A/B/C/D中的某种类型的异常
void fun() throw(A,B,C,D);
// 这里表示这个函数只会抛出bad_alloc的异常
void* operator new (std::size_t size) throw (std::bad_alloc);
// 这里表示这个函数不会抛出异常
void* operator delete (std::size_t size, void* ptr) throw();
// C++11 中新增的noexcept,表示不会抛异常,没有后缀表示可能会抛出异常
thread() noexcept;
thread (thread&& x) noexcept;