• 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
        
  • 相关阅读:
    用SSH指令批量修改文件夹 文件权限和拥有者
    magento转移服务器和magento建立多站点总结
    ssh 命令
    magento缓存系列详解:clean cache
    如何配置magento免运费商品方法
    Magento后台订单显示产品图片的修改方法
    如何在magento后台增加一个自定义订单状态
    Magento路径函数getBaseUrl使用方法
    图解HTTPS
    php 数组 添加元素、删除元素
  • 原文地址:https://www.cnblogs.com/bky-hbq/p/13623787.html
Copyright © 2020-2023  润新知