一、前言
异常处理使得程序在出现错误时能够得到及时的处理,以免程序崩溃或者未退出而引起更严重的错误。退出是无法接受的,严重的错误又很难查,这会使查问题的人崩溃。
二、为什么要使用异常
- 使检测问题和解决问题分离
- 使用返回码也可以,但是这容易导致信息不一致(0返回成功还是1返回成功,有的时候是相反的)
- 使用返回码占用返回值,c++可以使用一个参数来作为输出值,但这会使程序难懂
- 返回值可以人为忽略(或者忘记检查),这存在潜在风险,使得程序莫名其妙的退出而不知原因。异常不可人为忽略,这使得程序健壮些
- 异常信息丰富
- 异常可以传递, 这使得在底部出现的错误可以在上面的某一处集中处理即可,而不用层层处理
三、注意事项
四、基本语法
1.关键字
抛异常: throw
捕捉 :try/catch
2.函数声明
异常是在函数中抛出的,这些函数在声明时可以对内部的异常处理进行描述,即明确写出异常列表, 这有助于调用者了解内部机制。
1. void TestFunction();
表示该函数内部有可能抛出异常
2. void TestFunction() throw(int, char);
表示该函数内部可能抛出异常,但是若抛异常,那只能是int, char类型的,如果抛出其他类型的,可以正常编译,但是运行到该处时会调用terminate() ----> abort()
3. void TestFunction()throw();
表示函数内部用于不会抛出异常,若抛出,可以正常编译,但是运行到该处时会terminate() ----> abort()
4. void TestFunction()throw(...);
表示该函数内部有可能抛出任何类型的异常
3.捕捉及捕捉顺序
try{
throw E();
}catch(H h){
//什么时候能执行到此处?见下面4条
}catch(H2 h){
//H无法处理时会执行到此
}catch(...){
//所有类型的异常都会被此处捕捉,不会向上层传递了,一般用于清理工作
throw;//可以抛给上层处理
}
1. H和E类型相同。 如都是int
2. H是E的基类 。 一般不会这样调用,会产生切片(slice)效果,即信息不全
3. H 是 E的父类指针
4. H 是 E的引用。常用,如std::exception &e
异常只要被捕捉后就不会再向下传递, 所以如果本层无法处理, 那么可以在catch中重新抛出, 由上层处理, 再次抛出时, 只需要throw;即可,无需重新指定对象。
4.简单练习
class myException{ };
float divide(int a, int b)throw(myException)
{
if (0 == b)
{
throw myException();
}
else
{
return a/b;
}
}
try{
float value = divide(10,0);
}catch(myException &e){
std::cout << "myException occur : 0 should not be denominator/*分母*/"std::endl;
}
5.标准C++异常
std::exception是c++的标准异常类, 我们在写自己的异常类时,应该尽量继承之。
1. 内容丰富,避免重造车轮子
2. 有助于调用者理解
c++11中exception()类的定义如下(noexcept 就是 throw()):
class exception {
public:
exception () noexcept;
exception (const exception&) noexcept;
exception& operator= (const exception&) noexcept;
virtual ~exception();
virtual const char* what() const noexcept;
}
标准异常阶层体系如下:
当继承标准异常类时,应该重载父类的what函数和虚析构函数
virtual const char* what() const noexcept;
6. 实现自己的异常类
class TestException : public std::exception
{
public:
TestException(int code, const std::string &msg):m_code(code), m_msg(msg){}
virtual ~TestException() throw(){}
virtual const char* what() throw()
{
return m_msg.c_str();
}
virtual int code()
return m_code;
}
private:
std::string m_msg;
int m_code = -1;
}
五来自C++之父Bjarne Stroustrup的建议
节选自《The C++ Programming Language》 ——C++之父Bjarne Stroustrup
1. Don’t use exceptions where more local control structures will suffice; 当局部的控制能够处理时,不要使用异常;
2. Use the "resource allocation is initialization" technique to manage resources; 使用“资源分配即初始化”技术去管理资源;
3. Minimize the use of try-blocks. Use "resource acquisition is initialization" instead of explicit handler code; 尽量少用try-catch语句块,而是使用“资源分配即初始化”技术。
4. Throw an exception to indicate failure in a constructor; 如果构造函数内发生错误,通过抛出异常来指明。
5. Avoid throwing exceptions from destructors; 避免在析构函数中抛出异常。
6. Keep ordinary code and error-handling code separate; 保持普通程序代码和异常处理代码分开。
7. Beware of memory leaks caused by memory allocated by new not being released in case of an exception; 小心通过new分配的内存在发生异常时,可能造成内存泄露。
8. Assume that every exception that can be thrown by a function will be thrown; 如果一个函数可能抛出某种异常,那么我们调用它时,就要假定它一定会抛出该异常,即要进行处理。
9. Don't assume that every exception is derived from class exception; 要记住,不是所有的异常都继承自exception类。
10. A library shouldn't unilaterally terminate a program. Instead, throw an exception and let a caller decide; 编写的供别人调用的程序库,不应该结束程序,而应该通过抛出异常,让调用者决定如何处理(因为调用者必须要处理抛出的异常)。
11. Develop an error-handling strategy early in a design; 若开发一个项目,那么在设计阶段就要确定“错误处理的策略”。
兄弟刚刚写博客,望诸位道上兄弟支持鼓励,欢迎任何形式的反馈,欢迎留言探讨,技术至上!