搜索引擎网站推广定义,网站 设计 精髓,网站怎么增加关键词库,杭州网络公司排名目录
一#xff0c;背景
1.1 C语言处理错误的方式
1.2 C异常概念
二#xff0c;异常的使用
2.1 异常的简单使用
2.2 异常的匹配原则
2.3 异常抛对象 2.4 异常的重新抛出 2.5 异常安全
三#xff0c;自定义异常体系
四#xff0c;异常优缺点
4.1 优点
4.2 缺点
…目录
一背景
1.1 C语言处理错误的方式
1.2 C异常概念
二异常的使用
2.1 异常的简单使用
2.2 异常的匹配原则
2.3 异常抛对象 2.4 异常的重新抛出 2.5 异常安全
三自定义异常体系
四异常优缺点
4.1 优点
4.2 缺点
一背景
1.1 C语言处理错误的方式
在C语言中处理错误的基本方式有两种 ①终止程序如assert如果不符合要求直接终止程序 缺陷用户难以接收如发生内存错误除0错误时会直接中断程序代价太大 ②返回错误码如很多库的接口函数都是通过把错误码放到全局变量errno中表示错误 缺陷返回的只有一个错误码程序员很难甚至无法得知具体的错误信息。 1.2 C异常概念
异常是一种处理错误的方式当一个函数发现自己无法处理的错误时就可以抛出异常让函数的直接或间接地调用者处理这个错误。主要包括下面三个关键字
①throw当问题出现时通过这个关键字“抛出”具体地错误信息
②catch通过catch关键字捕获throw抛出地异常信息通过一个字符串或类接收然后根据信息针对处理
③trytry和catch配套使用throw在try中使用try后面一般跟着一个或多个catch捕获
二异常的使用
2.1 异常的简单使用
//throw:当问题出现时程序会抛出一个异常
//catch:捕获异常
//try:包含多个catch
double Division1(int a, int b)
{// 当b 0时抛出异常if (b 0)throw Division by zero condition!;elsereturn ((double)a / (double)b);
}void Func1()
{//如果Func里面也有捕获出异常的时候如果Func里有和throw抛出内容类型一样的就跳到Func里的如果没有匹配类型就继续往下找直到跳到匹配类型的catchtry{int len, time;cin len time;cout Division1(len, time) endl;}catch (int errid){cout errid endl;}cout Func1() end endl;
}void main()
{while (1){try{Func1();}//没有异常的时候直接跳过catchcatch (const char* errmsg){//如果throw了直接跳到离throw位置最近的catchcout errmsg endl;}允许多个catch对于throw的不同类型匹配不同的catch但是多个catch不允许有相同传参类型的catchcatch (int errid){cout errid endl;}catch (char errmsg){cout errmsg endl;}//如果throw的内容没有匹配类型的catch会直接报错直接终止程序catch (...) //捕获任意类型的异常 -- 防止出现未捕获异常时直接报错程序终止{cout 未知异常 endl;}}
} 2.2 异常的匹配原则
异常的抛出和匹配原则 ①异常是通过抛出对象而引发的该对象的类型决定了应该激活哪个catch的处理代码 ②被选中的处理代码是调用链中与该对象类型皮皮额且离throw位置最近的那一个 ③抛出异常对象后会生成一个异常对象的拷贝因为抛出的对象可能是一个临时对象所以会生成一个拷贝对象这个拷贝的临时对象会在被catch以后销毁。类似函数传值返回 ④catch(...)可以捕获任意类型的异常但缺点是无法知道错误类型 ⑤在实际抛出和捕获的匹配原则中有个例外可以抛出派生类对象使用基类捕获这个很实用后面讲 在函数调用链中异常栈匹配原则 ①首先检查throw本身是不是在try内部如果是再查找匹配的catch语句如果有匹配的调到catch的地方进行处理之后继续沿着catch子句后面继续执行 ②如果没有匹配的catch则退出当前函数栈继续再调用函数的栈中查找匹配的catch ③如果到达了main函数的栈依旧没有匹配的则终止程序。所以实际中我们最后都要加一个catch(...)捕获任意类型的异常 2.3 异常抛对象
class Exception
{
public:Exception(const string errmsg, int id):_errmsg(errmsg), _errid(id){}virtual string what() const{return _errmsg;}int GetErrid(){return _errid;}int getid()const{return _errid;}
protected:string _errmsg;int _errid;
};double Division2(int a, int b)
{//当b 0时抛出异常if (b 0){/*Exception e(除0错误, a);throw e;*/throw Exception(除0错误, a);}else{return ((double)a / (double)b);}
}void Func2()
{int len, time;cin len time;cout Division2(len, time) endl;
}//1,抛异常可以抛任意类型对象
//2捕获时要求类型匹配void main2()
{while (1){try{Func2();}catch (const Exception e){cout e.what() endl;}catch (...) // 捕获任意类型的异常 -- 防止出现未捕获异常时程序终止{cout 未知异常 endl;}}
} 2.4 异常的重新抛出
有时候抛异常了但是不是只要有异常了就报错比如我们发送消息的时候第一次发送失败不是直接就报错而是再多发几次多发几次都失败后才报错如下代码
void SeedMsg(const string s)
{// 要求出现网络错误重试三次srand(time(0));if (rand() % 3 0) //模拟服务出错偶然情况下满足这个if达到模拟错误的效果{throw HttpServerException(网络错误, 100, get);}else if (rand() % 4 0){throw HttpServerException(权限不足, 101, post);}cout 发送成功: s endl;
}void HttpServer1()
{// 要求出现网络错误重试3次string str 今晚一起看电影怎么样;int n 3;while (n--){try{SeedMsg(str);// 没有发生异常跳出循环break;}catch (const Exception e){// 网络错误 重试3次内if (e.getid() 100 n 0){continue;//这是网络错误后继续调SeedMsg然后继续网络错误重试}else{throw e; // 超过三次网络错误直接重新抛出}}}
}void main()
{while (1){//this_thread::sleep_for(chrono::seconds(1));Sleep(1000);try{HttpServer();}catch (const Exception e) // 这里捕获父类对象就可以{// 多态cout e.what() endl;// 记录日志}catch (...){cout Unkown Exception endl;}}
} 2.5 异常安全
①最好不要在构造函数中抛出异常否则可能导致对象不完整或没有初始化完成
②最好不要在析构函数内抛出异常否则可能导致资源泄漏
③C中异常经常会导致资源泄漏问题比如在new和delete中抛出了异常导致内存泄漏或者在lock和unlock抛异常导致死锁C经常使用RAII来解决上面的问题关于RAII我们到智能指针进行讲解
double Division(int a, int b)
{// 当b 0时抛出异常if (b 0){throw Division by zero condition!;}return (double)a / (double)b;
}void Func3()
{// 这里可以看到如果发生除0错误抛出异常另外下面的array没有得到释放。// 所以这里捕获异常后并不处理异常异常还是交给外面处理这里捕获了再// 重新抛出去。// 隐患第一个成功第二个失败//可以解决但是很麻烦这样的问题一般是用智能指针解决int* array1 new int[10];int* array2 new int[10];int len, time;cin len time;try{cout Division(len, time) endl;}catch (...){cout delete [] array1 endl;delete[] array1;cout delete [] array1 endl;delete[] array2;throw; // 捕获什么抛出什么}cout delete [] array1 endl;delete[] array1;cout delete [] array2 endl;delete[] array2;
}void main5()
{try{Func3();}catch (const char* errmsg){cout errmsg endl;}
}
三自定义异常体系
C库给我们提供了一系列标准的异常定义在std::exception中。在实际中我们可以去继承exception类实现自己的异常类。但是实际上很多公司有一套属于自己的异常继承体系因为C标准库给的并不好用。
很多公司都会自定义自己的异常体系进行规范的异常管理因为一个项目中如果大家都随意抛异常那么外层的调用者基本没法玩了。
下面就是简单模拟服务器开发中的异常体系
class Exception
{
public:Exception(const string errmsg, int id):_errmsg(errmsg), _errid(id){}virtual string what() const{return _errmsg;}int GetErrid(){return _errid;}int getid()const{return _errid;}
protected:string _errmsg;int _errid;
};class SqlException : public Exception //数据库的错误
{
public:SqlException(const string errmsg, int id, const string sql):Exception(errmsg, id) //必须要调用父类的构造函数来初始化父类, _sql(sql){}virtual string what() const{string str SqlException:;str _errmsg;str -;str _sql;return str;}
protected:const string _sql;
};class CacheException : public Exception//缓存区的错误
{
public:CacheException(const string errmsg, int id):Exception(errmsg, id){}virtual string what() const{string str CacheException:;str _errmsg;return str;}protected://stackstring _stPath;
};class HttpServerException : public Exception//网络服务错误
{
public:HttpServerException(const string errmsg, int id, const string type):Exception(errmsg, id), _type(type){}virtual string what() const{string str HttpServerException:;str _type;str :;str _errmsg;return str;}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(权限不足, 200);}else if (rand() % 6 0){throw CacheException(数据不存在, 201);}SQLMgr();
}void HttpServer() //网络服务
{// 模拟出错srand(time(0));if (rand() % 3 0){//throw HttpServerException(请求资源不存在, 100, get);throw HttpServerException(网络错误, 100, get);}else if (rand() % 4 0){throw HttpServerException(权限不足, 101, post);}CacheMgr();//重试根据重试次数来抛对应组的异常
}void SeedMsg(const string s)
{// 要求出现网络错误重试三次srand(time(0));if (rand() % 3 0) //模拟服务出错偶然情况下满足这个if达到模拟错误的效果{throw HttpServerException(网络错误, 100, get);}else if (rand() % 4 0){throw HttpServerException(权限不足, 101, post);}cout 发送成功: s endl;
}void main()
{while (1){Sleep(1000);try{HttpServer();}catch (const Exception e){//多态cout e.what() endl;}catch (...){cout Unknow Exception endl;}}
} 四异常优缺点
4.1 优点
①异常对象定义好了相比错误码的方式我们可以清晰准确地显示出错误的各种信息甚至可以包含堆栈的调用信息这样可以更好地定位程序地bug
②很多地第三方库都包含异常比如boostgtestgmock等等常用地库那么我们使用它们的时候也需要使用库
③部分函数使用异常可以更好地处理比如构造函数没有返回值不方便使用错误码的方式处理。比如T operator这样的函数如果pos越界了只能使用异常或终止程序处理没办法通过返回值表示错误
4.2 缺点
①异常会使我们的程序执行流乱跳一旦没使用好就会变得非常混乱并且是运行时乱跳所以可能会跳过我们调试的断点导致我们跟踪调试以及分析程序时比较困难
②异常会有一些性能的开销。当然现代硬件速度已经很快现在暂时不考虑
③C没有java的垃圾回收机制资源需要自己管理异常会导致资源泄漏的情况经常发送还有死锁等安全问题。这个我们到智能指针再说
④C标准库的异常体系定义得不好导致大家自定义各自得异常体系非常混乱
总结异常总体而言是利大于弊得所以工程中我们鼓励使用异常OO得语言基本都是用异常处理错误这也是大势所趋。