随意抛异常,那么外层的调用者基本就没办法玩了,所以实际中都会定义一套继承的规范体系。
这样大家抛出的都是继承的派生类对象,捕获一个基类就可以了。
这就有了用子类抛异常,父类来捕获异常。什么意思呢?来看例子:
//规定一个异常的标准类 class Exception { public: Exception(const string& errmsg, int id) :_errmsg(errmsg) , _id(id) {} virtual string what() const { return _errmsg; } protected: string _errmsg; int _id; }; //我么们可以通过继承父类后,增加子类的成员变量来区分异常类型 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; } private: 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; } }; 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("权限不足", 100); //throw 1; } 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) { Sleep(1000); try { HttpServer(); } catch (const Exception& e) // 这里捕获父类对象就可以 { // 多态 cout << e.what() << endl; } catch (...) { cout << "Unkown Exception" << endl; } } return 0; }
可以来看一下结果:
catch(...){}的作用就是方式其他无匹配的异常类型报错停止程序。
这里其实也用到了多态的调用,通过父类的引用,来调用重写以后的虚函数,从而实现多态调用。
这就很好的解决了问题。
C++ 提供了一系列标准的异常 ,我们可以在程序中使用这些标准的异常。
但是
实际中我们可以可以去继承 exception 类实现自己的异常类。但是实际中很多公司像上面一
样自己定义一套异常继承体系。因为 C++ 标准库设计的不够好用。
只需知道这些异常代表的意义:
(申请内存空间)
(越界访问)
5.异常安全
1.构造函数完成对象的构造和初始化,最好不要在构造函数中抛出异常,否则可能导致对象不
完整或没有完全初始化
2.析构函数主要完成资源的清理,最好不要在析构函数内抛出异常,否则可能导致资源泄漏(内
存泄漏、句柄未关闭等)
3.C++中异常经常会导致资源泄漏的问题,比如在new和delete中抛出了异常,导致内存泄
漏,在lock和unlock之间抛出了异常导致死锁,C++经常使用RAII来解决以上问题,关于RAII
会在智能指针进行讲解。(我们下期再见)
6.异常的优缺点
优点:
1. 异常对象定义好了,相比错误码的方式可以清晰准确的展示出错误的各种信息,甚至可以包含堆栈调用的信息,这样可以帮助更好的定位程序的bug。
2. 返回错误码的传统方式有个很大的问题就是,在函数调用链中,深层的函数返回了错误,那
么我们得层层返回错误,最外层才能拿到错误。但C++的异常可以直接跳转到捕获异常的位置。
错误码返回要层层判断,当前遇到错误,返回上一层要判断返回的错误码。
缺点:
1. 异常会导致程序的执行流乱跳,并且非常的混乱,并且是运行时出错抛异常就会乱跳。这会
导致我们跟踪调试时以及分析程序时,比较困难。
2. 异常会有一些性能的开销。当然在现代硬件速度很快的情况下,这个影响基本忽略不计。
3. C++没有垃圾回收机制,资源需要自己管理。有了异常非常容易导致内存泄漏、死锁等异常
安全问题。这个需要使用RAII来处理资源的管理问题。学习成本较高。
总之,利大于弊!