C++ Primer 有感(异常处理)(二)

时间:2021-08-04 15:48:26

异常就是运行时出现的不正常,例如运行时耗尽了内存或遇到意外的非法输入。异常存在于程序的正常功能之外,并要求程序立即处理。不能不处理异常,异常是足够重要的,使程序不能继续正常执行的事件。如果找不到匹配的catch,程序就调用库函数terminate。

C++ Primer 有感(异常处理)(二)

如果不处理异常将会出现上图的效果。下面是处理异常的代码:

  1. #include<iostream>
  2. using namespace std;
  3. int main()
  4. {
  5. try
  6. {
  7. int a=3,b=0;
  8. int c=a/b;
  9. }
  10. catch(...)
  11. {
  12. }
  13. return 0;
  14. }

这里catch后圆括号中的...指的是捕获所有异常。

抛出异常的时候,将暂停当前函数的执行,开始查找匹配的catch子句。首先检查throw本身是否在try块内部,如果是,检查与该try相关的catch子句,看是否其中之一与被抛出对象相匹配。如果找到匹配的catch,就处理异常:如果找不到,就退出当前函数(释放当前函数的内存并撤销局部对象),并继续在调用函数中查找。

如果对抛出异常的函数调用是在try块中,则检查与该try块相关的catch子句。如果找到匹配的catch就处理异常;如果找不到匹配的catch,调用函数也退出,并且继续在调用这个函数的函数中查找。

这个过程称之为栈展开,沿嵌套函数调用链继续向上,直到为异常找到一个catch子句。只要找到能够处理异常的catch子句,就进入该catch子句,并在该处理代码中继续执行。当catch子句结束的时候,在紧接在与该try块相关的最后一个catch子句之后的点继续执行。

在栈展开的时候,会退出某个函数,释放当前内存并撤销局部对象,这时会调用对象的析构函数,如果析构函数抛出异常,将会调用标准库terminate函数,强制整个程序非正常退出。所以析构函数应该从不抛出异常。

在查找匹配的catch期间,找到的catch不必是与异常最匹配的那个catch,相反,将选中第一个找到的可以处理该异常的catch。因此,在catch子句列表中,最特殊的catch必须最先出现,否则没有执行的机会。

进入catch的时候,用异常对象初始化catch的形参。因为基类的异常说明符可以捕获派生类的异常对象。如果异常对象是引用,则可以使用多态,调用基类的virtual将执行派生类的覆盖的函数。若catch的异常说明符是对象,则将派生类对象分割为它的基类对象。

异常的层次

异常的层次如下图:

C++ Primer 有感(异常处理)(二)

exception类型所定义的唯一操作是一个名为what的虚成员,该函数返回const char* 对象,它一般返回用来在抛出位置构造异常对象的信息。因为what是虚函数,如果捕获了基类类型的引用,对what函数的调用将执行适合异常对象的动态类型的版本。

使用自定义异常

下面自定义异常类,继承自logic_error,MyException.h如下:

  1. #if !defined(AFX_MYEXCEPTION_H__EB39C92B_B867_41AA_83A6_E614FD4AEC63__INCLUDED_)
  2. #define AFX_MYEXCEPTION_H__EB39C92B_B867_41AA_83A6_E614FD4AEC63__INCLUDED_
  3. #if _MSC_VER > 1000
  4. #pragma once
  5. #endif // _MSC_VER > 1000
  6. #include<string>
  7. #include<stdexcept>
  8. class MyException:public std::logic_error
  9. {
  10. public:
  11. MyException(std::string s,std::string myinfo);
  12. const char* what() const;
  13. virtual ~MyException();
  14. private:
  15. std::string myinfo;
  16. };
  17. #endif // !defined(AFX_MYEXCEPTION_H__EB39C92B_B867_41AA_83A6_E614FD4AEC63__INCLUDED_)

MyException.cpp内容如下:

  1. #include "MyException.h"
  2. MyException::MyException(std::string s,std::string myinfo):logic_error(s)
  3. {
  4. this->myinfo=myinfo;
  5. }
  6. MyException::~MyException()
  7. {
  8. }
  9. const char* MyException::what() const
  10. {
  11. return myinfo.c_str();
  12. }

main函数所在的文件内容如下:

  1. #include<iostream>
  2. #include"MyException.h"
  3. using namespace std;
  4. void getException(bool b)
  5. {
  6. if(b)
  7. {
  8. throw MyException("error","the parameter is true");
  9. }
  10. }
  11. int main()
  12. {
  13. try
  14. {
  15. getException(1);
  16. }
  17. catch(logic_error &e)
  18. {
  19. cout<<e.what()<<endl;
  20. }
  21. return 0;
  22. }

运行结果如下:

C++ Primer 有感(异常处理)(二)

在MyException类中,覆盖了logic_error类中的virtual函数,在catch异常说明类型使用的是基类logic_error的引用,所以输出的结果是MyException中myinfo的内容。这就是多态在异常中的使用。

C++ Primer 有感(异常处理)(二)