目录
(1)实际中很多公司都会自定义自己的异常体系进行规范的异常管理
实际中C语言基本都是使用返回错误码的方式处理错误,部分情况下使用终止程序处理非常严重的错误。
异常是面向对象语言常用的一种处理错误的方式,当一个函数发现自己无法处理的错误时就可以抛出异常,让函数直接或间接的调用者处理这个错误。
- try
- {
- //被保护的代码
- }
- catch (ExceptionName e1)
- {
- //catch块
- }
- catch (ExceptionName e2)
- {
- //catch块
- }
- catch (ExceptionName eN)
- {
- //catch块
- }
①异常栈展开测试
- void func1()
- {
- throw string("异常测试"); //返回一个匿名对象
- }
-
- void func2()
- {
- func1();
- }
-
- void func3()
- {
- func2();
- }
-
- int main()
- {
- try
- {
- func3();
- }
- catch (const string& s)
- {
- cout << "错误描述:" << s << endl;
- }
- catch (...)
- {
- cout << "未知异常" << endl;
- }
- return 0;
- }
②func1中的异常抛出后的执行过程

①有时候单个的catch可能不能完全处理一个异常,在进行一些校正处理以后,希望再交给更外层的调用链函数来处理,比如最外层可能需要拿到异常进行日志信息的记录,这时就需要通过重新抛出将异常传递给更上层的函数进行处理
②如果直接让最外层捕获异常进行处理可能会引发一些问题
- void func1()
- {
- throw string("异常测试");
- }
-
- void func2()
- {
- int* array = new int[10];
- func1();
-
- //do something...
-
- delete[] array;
- }
-
- int main()
- {
- try
- {
- func2();
- }
- catch (const string& s)
- {
- cout << s << endl;
- }
- catch (...)
- {
- cout << "未知异常" << endl;
- }
- return 0;
- }
③可以在func2中先对func1抛出的异常进行捕获,捕获后先将申请到的内存释放再将异常重新抛出,这时就避免了内存泄露
- void func2()
- {
- int* array = new int[10];
- try
- {
- func1();
- //do something...
- }
- catch (...) //拦截异常,不是要处理异常 ; 捕获任意类型的异常,补什么,抛什么
- {
- delete[] array;
- throw; //将捕获到的异常再次重新抛出
- }
-
- delete[] array;
- }
将抛异常导致的安全问题叫做异常安全问题,对于异常安全问题给出建议:
为了让函数使用者知道某个函数可能抛出哪些类型的异常,C++标准规定:
- //表示func函数可能会抛出A/B/C/D类型的异常
- void func() throw(A, B, C, D);
-
- //表示这个函数只会抛出bad_alloc的异常
- void* operator new(std::size_t size) throw(std::bad_alloc);
-
- //表示这个函数不会抛出异常
- void* operator new(std::size_t size, void* ptr) throw();

- #include
- #include
- #include
- #include
- #include
- using namespace std;
-
- // 服务器开发中通常使用的异常继承体系
- 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 student where name = '张三'");
- }
- else if (rand() % 8 == 0)
- {
- throw SqlException("sql语句错误", 101, "select * from age = 21");
- }
-
- }
-
- 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();
- }
-
- void ServerStart()
- {
- while (1)
- {
- this_thread::sleep_for(chrono::seconds(1)); //每隔1s 执行一次
-
- try {
- HttpServer();
- }
- catch (const Exception& e) // 这里捕获父类对象就可以
- {
- // 多态
- cout << e.what() << endl;
- }
- catch (...)
- {
- cout << "Unkown Exception" << endl;
- }
- }
- }
-
- int main()
- {
- ServerStart();
-
- return 0;
- }

(1)C++标准库当中的异常也是一个基础体系,其中exception就是各个异常类的基类,我们可以在程序中使用这些标准的异常

(2)对继承体系中出现的每个异常的说明
| 异常 | 描述 |
|---|---|
| std::exception | 该异常是所有标准C++异常的父类。 |
| std::bad_alloc | 该异常可以通过new抛出。 |
| std::bad_cast | 该异常可以通过dynamic_cast抛出。 |
| std::bad_exception | 这在处理C++程序中无法预期的异常时非常有用。 |
| std::bad_typeid | 该异常可以通过typeid抛出。 |
| std::logic_error | 理论上可以通过读取代码来检测到的异常。 |
| std::domain_error | 当使用了一个无效的数学域时,会抛出该异常。 |
| std::invalid_argument | 当使用了无效的参数时,会抛出该异常。 |
| std::length_error | 当创建了太长的std::string时,会抛出该异常。 |
| std::out_of_range | 该异常可以通过方法抛出,例如std::vector和std::bitset<>::operator。 |
| std::runtime_error | 理论上不可以通过读取代码来检测到的异常。 |
| std::overflow_error | 当发生数学上溢时,会抛出该异常。 |
| std::range_error | 当尝试存储超出范围的值时,会抛出该异常。 |
| std::underflow_error | 当发生数学下溢时,会抛出该异常。 |
(3)补充
(3) 但总体而言,异常的利大于弊,所以工程中我们还是鼓励使用异常的