2 异常类

1 异常简介

  • C++内置了异常处理的语法元素 try...catch...

    • try 语句处理正常代码逻辑
    • catch 语句处理异常情况
    • try 语句中的异常由对应的 catch 语句处理
  • 示例

    try {
        // try语句中的代码块有可能发生异常,该异常由此间代码块中的throw语句抛出(如divide函数中的throw),由try语句接收,再由catch处理
        double r = divide(1,0); 
    }
    catch(...){
        cout << "Divided by zero.." << endl;
    }
    
  • C++ 通过 throw 语句抛出异常信息

    double divided(double a,double b){
        const double delta = 0.000000000000001;
        double ret = 0;
        
        if(!((-delta < b) && (b < delta))) {
            ret = a / b;
        }
        else {
            throw 0;  // 产生除 0 异常,对应一个值:0,函数此时立即返回
        }
        return ret;
    }
    

2 C++ 异常处理分析

  • throw 抛出的异常必须被 catch 处理

    • 当前函数能够处理异常,程序继续往下执行
    • 当前函数无法处理异常(没有 try...catch...,则函数停止执行,并返回
  • 未被处理的异常会顺着函数调用栈向上传播,直到被处理为止,否则程序将停止执行

  • 示例1:C++ 中的异常处理机制

    • Demo1:未处理异常

      #include <iostream>
      
      using namespace std;
      
      double divide(double a, double b)
      {
          const double delta = 0.000000000000001;
          double ret = 0;
      
          if( !((-delta < b) && (b < delta)) ) {
              ret = a / b;
          }
          else {
              throw 0;   // 产生除 0 异常
          }
      
          return ret;
      }
      
      int main()
      {
          cout << "main() begin" << endl;
          
          divide(1, 0);
      
          cout << "main() end" << endl;
      
          return 0;
      }
      
    • 运行

      • 分析:报错原因:由于 divide 函数中抛出了一个异常,divide 函数立即返回(返回一个 0 值)到 main 函数中,但是 main 函数中没有 tyr..catch... 语句块,main 函数向上传递异常,直接报错!
      main() begin
      报错
      
    • Demo2:添加 try...catch... 语句块

      #include <iostream>
      
      using namespace std;
      
      double divide(double a, double b)
      {
          const double delta = 0.000000000000001;
          double ret = 0;
      
          if( !((-delta < b) && (b < delta)) ) {
              ret = a / b;
          }
          else {
              throw 0;   // 产生除 0 异常
          }
      
          return ret;
      }
      
      int main()
      {
          cout << "main() begin" << endl;
      
          try {
              double c = divide(1, 0);
              cout << "c = " << c << endl;
          }
          catch(...) {
              cout << "Divided by zero..."  << endl;
          }
          
          try {
              double c = divide(1, 1);
              cout << "c = " << c << endl;
          }
          catch(...) {
              cout << "Divided by zero..."  << endl;
          }
      
          cout << "main() end" << endl;
      
          return 0;
      }
      
    • 运行

      main() begin
      Divided by zero...
      c = 1
      main() end
      

3 异常处理的规则

  • 同一个 try 语句可以跟上多个 catch 语句

    • catch 语句可以定义具体处理的异常类型
    • 不同类型的异常由不同的 catch 语句负责处理
    • try 语句中可以抛出任何类型的异常
    • catch(...) 用于处理所有类型的异常
    • 任何异常都只能被捕获(catch一次
  • 异常处理的匹配规则

    • 异常抛出后,至上而下严格匹配每一个 catch 语句处理的类型
    • 异常处理匹配时,不进行任何的类型转换
  • 示例2:多个 catch 语句情况

    • Demo

      #include <iostream>
      
      using namespace std;
      
      double divide(double a, double b)
      {
          const double delta = 0.000000000000001;
          double ret = 0;
      
          if( !((-delta < b) && (b < delta)) ) {
              ret = a / b;
          }
          else {
              throw 0;   // 产生除 0 异常
          }
      
          return ret;
      }
      
      // 异常类型是严格匹配的
      void test1()
      {
          try {
              // 抛出 char 类型异常
              throw 'c';
          }
          // 处理 int 类型异常
          catch(int i) {
              cout << "catch(int i)" << endl;
          }
          // 处理 double 类型异常
          catch(double d) {
              cout << "catch(double d)" << endl;
          }
          // 处理 char 类型异常
          catch(char c) {
              cout << "catch(char c)" << endl;
          }
      }
      
      // 任何异常都只能被捕获(catch)一次
      void test2()
      {
          try {
              // 抛出字符串(字符串字面常量:const char*)异常
              throw "ABCD";
          }
          catch(char* c) {
              cout << "catch(char* c)" << endl;
          }
          catch(const char* cc) {
              cout << "catch(const char* cc)" << endl;
          }
          // 能够处理所有类型异常的 catch 放在最后
          catch(...) {
              cout << "catch(...)" << endl;
          }
      }
      
      int main()
      {
          test1();
          
          test2();
      
          return 0;
      }
      
    • 运行

      catch(char c)
      catch(const char* cc)
      

4 数据结构中的异常类构建

  • 除基本类型外,异常的类型还可以是自定义类类型

  • 对于类类型异常的匹配依旧是至上而下严格匹配

  • (面向对象中的)赋值兼容性原则(子类对象赋值给父类对象)在异常匹配中依然能适用

  • 一般而言:匹配子类异常的 catch 放在上部 ,匹配父类异常的 catch 放在下部 => ArithmeticException 类的使用

  • 现代 C++ 库必然包含充要的异常类族,异常类是数据结构类所依赖的“基础设施”

  • 异常类功能定义

    异常类 功能定义
    ArithmeticException 计算异常
    NullPointerException 空指针异常
    IndexOutOfBoundsException 越界异常
    NoEnoughMemoryException 内存不足异常
    InvalidParameterException 参数错误异常
  • 异常类中的接口定义

    class Exception
    {
    public:
        Exception(const char* message);
        Exception(const char* file,int line);
        Exception(const char* message,const char* file,int line);
        
        Exception(const Exception& e);
        Exception& operator= (const Exception& e);
        
        virtual const char* message() const;   // 异常的详细信息
        virtual const char* location() const;  // 产生异常的文件及相应的行号
        
        virtual ~Exception() = 0;  // 纯虚函数,说明当前类是一个抽象类 
    }
    
  • 创建异常类族

    • Exception 抽象类

      //Exception.h
      #ifndef _EXCEPTION_H_
      #define _EXCEPTION_H_
      
      namespace DTLib
      {
          
      class Exception
      {
      protected:
          //成员变量:说明当前异常的详细信息
      	char* m_message;
      	//成员变量:说明发生异常的地点
      	char* m_location;
      	//辅助函数:用于重载构造函数
      	void init(const char* message, const char* file, int line);
      public:
          //3个构造函数
      	Exception(const char* message);
      	Exception(const char* file, int line);
      	Exception(const char* message, const char* file, int line);
      	//拷贝构造函数
      	Exception(const Exception& e);
      	//重载“=”操作符
      	Exception& operator= (const Exception& e);
      
      	virtual const char* message() const;
      	virtual const char* location() const;
      
      	//这里为了做测试,改为虚函数
      	virtual ~Exception();
      };
          
      }
      #endif
      
      //Exception.cpp
      #include "Exception.h"
      #include <cstring>
      #include <cstdlib>
      using namespace std;
      
      namespace DTLib
      {
      	void Exception::init(const char* message, const char* file, int line) {
      		//拷贝字符串,此时m_message指向的字符串(与message所指向的字符串相同)在堆中(而message可能在堆中,也有可能在全局数据区中),由m_essage自己控制
      		m_message = strdup(message);
      		if (file != NULL) {
      			//将发生异常的行号转换为字符串
      			//定义一个辅助字符数组,保存发生异常的文件名
      			char sl[16] = { 0 };
      			itoa(line, sl, 10);
      			m_location = static_cast<char*>(malloc(strlen(file) + strlen(sl) + 2));
                  //动态内存申请成功
                  if(m_location != NULL) {
                      m_location = strcpy(m_location, file);
      			    m_location = strcat(m_location, ":");
      			    m_location = strcat(m_location, sl);
                  }
      		}
      		else {
      			m_location = NULL;
      		}
      	}
      
      	Exception::Exception(const char* message) {
      		init(message, NULL, 0);
      	}
      	Exception::Exception(const char* file, int line) {
      		init(NULL, file, line);
      	}
      	Exception::Exception(const char* message, const char* file, int line) {
      		init(message, file, line);
      	}
      
      	//用于深拷贝
      	Exception::Exception(const Exception& e) {
      		m_message = strdup(e.m_message);
      		m_location = strdup(e.m_location);
      
      	}
      	Exception& Exception::operator= (const Exception& e) {
      		if (this != &e) {
      			free(m_message);
      			free(m_location);
      			m_message = strdup(e.m_message);
      			m_location = strdup(e.m_location);
      		}
      		return *this;
      	}
      
      	const char* Exception::message() const {
      		return m_message;
      	}
      	const char* Exception::location() const {
      		return m_location;
      	}
      
      	//纯虚函数不需要实现,但析构函数必须提供实现
      	Exception:: ~Exception() {
      		free(m_message);
      		free(m_location);
      	}
      }
      
    • Exception 类的使用

      #include <iostream>
      #include "Exception.h"
      using namespace std;
      using namespace DTLib;
      
      int main()
      {
          try{
              throw Exception("test",__FILE__,__LINE__);
          }
        catch(const Exception& e){
              cout << "catch(const Exception& e)" << endl;
              cout << e.message() << endl;
              cout<< e.location() << endl;
        }
      
          return 0;
      }
      
    • 运行输出

      catch(const Exception& e)
      test
      ***main.cpp:15
      
    • Exception 抽象类改进:利用宏,在需要的地方,直接扔出异常的类型和信息即可,不需要再管理文件名和行号(宏直接添加了)

      //Exception.h
      #ifndef _EXCEPTION_H_
      #define _EXCEPTION_H_
      
      namespace DTLib
      {
      //定义一个宏:说明异常信息,第一个参数e为异常类型,第二个参数m为异常的详细信息
      #define THROW_EXCEPTION(e,m) (throw e(m,__FILE__,__LINE__))
      class Exception
      {
      protected:
      	char* m_message;
      	char* m_location;
      	void init(const char* message, const char* file, int line);
      public:
      	Exception(const char* message);
      	Exception(const char* file, int line);
      	Exception(const char* message, const char* file, int line);
      	Exception(const Exception& e);
      	Exception& operator= (const Exception& e);
      	virtual const char* message() const;
      	virtual const char* location() const;
      	virtual ~Exception();
      };
      
      }
      
      #endif
      
    • 改进的 Exception 类的使用

      #include <iostream>
      #include "Exception.h"
      using namespace std;
      using namespace DTLib;
      
      int main()
      {
          try{
              //用宏抛出异常
              THROW_EXCEPTION(Exception,"test");
          }
        catch(const Exception& e){
              cout << "catch(const Exception& e)" << endl;
              cout << e.message() << endl;
              cout<< e.location() << endl;
        }
      
          return 0;
      }
      
    • ArithmeticException

      //Exception.h
      namespace DTLib
      
      {
      
      class ArithmeticException : public Exception
      {
      public:
          //无参构造
          ArithmeticException() : Exception(0,0,0) {}
          //有参构造
          ArithmeticException(const char* message) : Exception(message,0,0) {}
          ArithmeticException(const char* file,int line) : Exception(0,file,0) {}
          ArithmeticException(const char* message,const char* file,int line) : Exception(message,file,line) {}
          //拷贝构造
          ArithmeticException(const ArithmeticException& e) : Exception(e) {}
          //"="
          ArithmeticException& operator= (const ArithmeticException& e){
              Exception::operator=(e);
              return *this;
          }
      };
      }
      
    • AithmeticException 类的使用

      //main.cpp
      int main()
      {
          try{
              THROW_EXCEPTION(ArithmeticException,"test");
          }
          //匹配子类异常的 catch 放在上部 ,匹配父类异常的 catch 放在下部
          catch(const ArithmeticException& e){
              cout << "catch(const ArithmeticException& e)" << endl;
              cout << e.message() << endl;
              cout << e.location() << endl;
          }
          //用子类对象赋值给父类对象
          catch(const Exception& e){
              cout << "catch(const Exception& e)" << endl;
              cout << e.message() << endl;
              cout << e.location() << endl;
          }
          return 0;
      }
      
    • 运行输出

      catch(const ArithmeticException& e)
      test
      ***main.cpp:15
      
原文地址:https://www.cnblogs.com/bky-hbq/p/13623787.html