技术交流QQ群:1027579432,欢迎你的加入!

1.Cpp中的异常处理

  • 异常是程序在执行期间产生的问题。C++异常是指在程序运行时发生的特殊情况,比如尝试除以零的操作。异常提供了一种转移程序控制权的方式。C++异常处理涉及到三个关键字:try、catch、throw
    • throw: 当问题出现时,程序会抛出一个异常,这是通过使用throw关键字来完成的。
    • catch: 在想要处理问题的地方,通过异常处理程序捕获异常,catch关键字用于捕获异常。
    • try: try块中的代码标识将被激活的特定异常,它后面通常跟着一个或多个catch块。
  • 如果有一个块抛出一个异常,捕获异常的方***使用try和catch关键字。try块中放置可能抛出异常的代码,try块中的代码被称为保护代码。如果try块在不同的情境下会抛出不同的异常,这个时候可以尝试罗列多个catch语句,用于捕获不同类型的异常。使用try/catch语句的语法如下所示:
        try{
            // 保护代码
        }catch(ExceptionName1 e1){
            // catch块1
        }catch(ExceptionName2 e2){
            // catch块2
        }catch(ExceptionNamen en){
            // catcth块n
        }
    

2.抛出异常

  • 可以使用throw语句在代码块中的任何地方抛出异常。throw语句的操作数可以是任意的表达式,表达式的结果的类型决定了抛出的异常的类型。以下是尝试除以零时抛出异常的实例:
        double division(int a, int b){
        if(b == 0)
            throw "Division by zero condition!";
        return (a / b);
        }
    
        int main(){
            int a = 3;
            int b = 0;
            cout << "division(3, 0) = " << division(a, b) << endl;
            return 0;
        }
    

3.捕获异常

  • catch块跟在try块后面,用于捕获异常。可以指定想要捕捉的异常类型,这是由catch关键字后的括号内的异常声明决定的。
        try{
            // 保护代码
        }catch(ExceptionName e){
            // 处理异常的代码
        }
    
  • 上面的代码会捕获一个类型为ExceptionName的异常。如果想让catch块能够处理try块抛出的任何类型的异常,则必须在异常声明的括号内使用省略号...,如下所示:
        double division(int a, int b){
        if(b == 0)
            throw "Division by zero condition!";
        return (a / b);
        }
        
        try{
            // 保护代码
        }catch(...){
            // 能处理任何异常的代码
        }
    
  • 下面是一个实例,抛出一个除以零的异常,并在catch块中捕获该异常
        try{
                z = division(3, 0);
            }catch(const char* msg){  // 常指针,等价于 char const *msg;
                cerr << msg << endl;
            }
    
  • 由于抛出了一个异常类型为const char*的异常,因此,当捕获该异常时,必须在catch块中使用const char*。当上面的代码被编译和执行时,它会产生下列结果:Division by zero condition!

4.Cpp中标准的异常

  • C++提供了一系列标准的异常,定义在<exception>中,可以在程序中使用这些标准的异常。它们是以父子类层次结构组织起来的,如下所示:


    基类-派生类异常组织结构.png
  • 下表是对上面层次结构中出现的每个异常的说明:


    详细说明.png

5.定义新的异常

  • 可以通过继承和重载exception类来定义新的异常。下面的实例演示了如何使用std::exception类来实现自己的异常:
        #include "iostream"
        #include "exception"
    
    
        using namespace std;
    
        // 自定义新的异常
        struct MyException: public exception{
            const char * what() const throw(){  // what()是异常类提供的一个公共方法,它已被所有子异常类重载,这将会返回异常产生的原因。
                return "C++ Exception!";
            }
        };
        try{
            throw MyException();
        }catch(MyException &e){
            cout << "MyException caught: ";
            cout << e.what() << endl; 
        }
    
  • what()是异常类提供的一个公共方法,它已被所有子异常类重载,这将会返回异常产生的原因。
  • const throw()不是函数,这个东西叫异常规格说明,表示what函数可以抛出异常的类型,类型说明放到()里,这里面没有类型,就是声明这个函数不抛出异常;通常函数不写后面的就表示函数可以抛出任何类型的异常
  • 异常规格说明:
    • 异常规格说明的目的是为了让函数使用者知道该函数可能抛出的异常有哪些。可以在函数的声明中列出这个函数可能抛掷的所有异常类型。例如:
          void fun() throw(A, B, C, D);
      
    • 若无异常接口声明,则此函数可以抛掷任何类型的异常
    • 不抛掷任何类型异常的函数声明如下:
          // 基类
          class MyException{
              public:
                  MyException(const char * msg): message(msg){
                      cout << "构造函数MyException类的对象.....\n";
                  }
                  MyException(const MyException &other): message(other.message){
                      cout << "拷贝构造函数MyException类的对象......\n";
                  }
                  // 析构函数
                  virtual ~MyException(){
                      cout << "~MyException.....\n";
                  }
      
                  const char *what() const{
                      return message.c_str();
                  }
              private:
                  string message;
          };
          // 派生类
          class MyExceptionD: public MyException{
              public:
                  MyExceptionD(const char *message): MyException(message){
                      cout << "构造函数MyExceptionD类的对象......\n";
                  }
                  MyExceptionD(const MyExceptionD & other): MyException(other){
                      cout << "拷贝构造函数MyExceptionD类的对象......\n";
                  }
                  // 析构函数
                  ~MyExceptionD(){
                      cout << "~MyExceptionD......\n";
                  }
          };
      
          void fun(int n) throw(int,  MyException, MyExceptionD){
              if(n == 1)
                  throw 1;
              else if(n == 2)
                  throw MyException("Test Exception......");
              else if(n == 3)
                  throw MyExceptionD("Test MyExceptionD......");
          }   
      
          void fun2() throw(){
      
          }
      
          try{
          fun(2);
          }catch(int n){
              cout << "catch int......\n";
              cout << "n = " << n << endl;
          }catch(MyExceptionD &e){
              cout << "catch MyExceptionD......\n";
              cout << e.what() << endl;
          }catch(MyException &e){
              cout << "catch MyException......\n";
              cout << e.what() << endl;
          }
      
  • 在C++11中,声明一个函数不可以抛出任何异常使用关键字noexcept,见下面的实例:
        void mightThrow(); // could throw any exceptions.
        void doesNotThrow() noexcept; // does not throw any exceptions.
    
  • 下面两个函数声明的异常规格在语义上是相同的,都表示函数不抛出任何异常。
        void old_stytle() throw();
        void new_style() noexcept;