• 第十一课 异常类构建


      当代C++库基本上都会使用C++里面的异常特性,依赖于异常特性所创建的库的稳定性是非常好的。因此,我们创建的库也要引入异常类族。本节中,我们就给DTLib添加异常类族。

      异常的类型可以是自定义的类类型

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

      赋值兼容性原则在异常匹配中依然适用

      一般而言

        匹配子类异常的catch放在上部

        匹配父类异常的catch放在下部

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

    顶层的父类是一个抽象类,不能定义对象,它是用来被继承的。通过继承的方式定义了5个异常类。分别是:

      计算异常

      越界异常

      内存不足异常

      参数错误异常

      空指针异常

    一般而言这几种异常足够了。

    具体如下:

    计算异常:例如,1除以0的时候,可以抛出这个异常

    空指针异常:如果我们在需要合法的指针时,得到的却是一个空指针,则可以抛出这种异常。

    越界异常:访问数组时,有可能越界,越界的情况下就可以抛出这种异常

    内存不足异常:动态的申请内存是,内存不足时,就抛出这种异常

    参数异常错误:我们编写的算法肯定要接收参数,作为好的习惯,肯定要判断一下参数的合法性,不合法的参数,要抛出这个异常

    异常类中的接口定义如下所示:

    可以看到,析构函数是一个虚函数,而且是一个纯虚的析构函数。纯虚的析构函数仅仅用来说明当前的类是一个抽象类。其它没有任何更多的功能。

     异常类的定义如下:

    #ifndef EXCEPTION_H
    #define EXCEPTION_H
    
    namespace DTLib
    {
    
    #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() = 0;
    };
    
    class ArithmeticException : public Exception
    {
    public:
        ArithmeticException() : Exception(0, 0, 0) {}
        ArithmeticException(const char* message) : Exception(message) {}
        ArithmeticException(const char* file, int line) : Exception(file, line) {}
        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;
        }
    };
    
    }
    
    #endif // EXCEPTION_H

    Exception.cpp文件如下:

     1 #include "Exception.h"
     2 #include <cstring>
     3 #include <cstdlib>
     4 
     5 using namespace std;
     6 
     7 namespace DTLib
     8 {
     9 
    10 void Exception::init(const char *message, const char *file, int line)
    11 {
    12     m_message = strdup(message);
    13 
    14     if(file != NULL)
    15     {
    16         char sl[16] = {0};
    17 
    18         itoa(line, sl, 10);
    19 
    20         m_location = static_cast<char*>(malloc(strlen(file) + strlen(sl) + 2));
    21         m_location = strcpy(m_location, file);
    22         m_location = strcat(m_location, ":");
    23         m_location = strcat(m_location, sl);
    24     }
    25     else
    26     {
    27         m_location = NULL;
    28     }
    29 }
    30 
    31 Exception::Exception(const char *message)
    32 {
    33     init(message, NULL, 0);
    34 }
    35 
    36 Exception::Exception(const char *file, int line)
    37 {
    38     init(NULL, file, line);
    39 }
    40 
    41 Exception::Exception(const char *message, const char *file, int line)
    42 {
    43     init(message, file, line);
    44 }
    45 
    46 Exception::Exception(const Exception &e)
    47 {
    48     m_message = strdup(e.m_message);
    49     m_location = strdup(e.m_location);
    50 }
    51 
    52 Exception& Exception::operator =(const Exception& e)
    53 {
    54     if(this != &e)
    55     {
    56         free(m_message);
    57         free(m_location);
    58 
    59         m_message = strdup(e.m_message);
    60         m_message = strdup(e.m_location);
    61     }
    62 
    63     return *this;
    64 }
    65 
    66 const char* Exception::message() const
    67 {
    68     return m_message;
    69 }
    70 
    71 const char* Exception::location() const
    72 {
    73     return m_location;
    74 }
    75 
    76 Exception::~Exception()
    77 {
    78     free(m_message);
    79     free(m_location);
    80 }
    81 
    82 }

    根据C++各种教程中的知识,纯虚函数不需要提供实现,纯虚函数等着子类来实现。但是在Exception.cpp中我们提供了纯虚函数(纯虚析构函数)的函数体,也就是给出了实现,这并不是一种错误的做法,这只是一种例外,因为这个纯虚函数是析构函数。C++规定,但凡我们定义了析构函数,不管析构函数是不是纯虚的,一定要提供实现,这是因为在析构一个对象的时候,最终会调用到父类的析构函数,如果父类的析构函数是纯虚的而且没有具体实现,那这一系列的析构是无法完成的。当调用到顶层父类的析构函数时,如果没有实现,这是一种很奇怪的现象。

      init函数中,第12行,我们使用了strdup函数将message的内容在堆空间中复制一份。因为,这个函数传入的message的内容可能是在栈上的,所以我们要复制一份。

    第18行将整形数转换为字符串,第20行申请内存空间,最后的+2是因为,我们需要写入“:”以及最后的结束符。第21行是字符串复制,第22、23行是字符串拼接。

      Exception.h中的THROW_EXCEPTION(e, m) (throw e(m, __FILE__, __LINE__))是一个宏,其中e代表的是异常类的类型,m代表我们自定义的message信息。__FILE__由编译器解析,会将文件名以字符串的形式写到该处。__LINE__也是由编译器处理,会将行号以int型数据写到该处。

    测试程序如下:

     1 #include <iostream>
     2 #include "Exception.h"
     3 using namespace std;
     4 using namespace DTLib;
     5 
     6 
     7 int main()
     8 {
     9     try
    10     {
    11         THROW_EXCEPTION(ArithmeticException, "test");
    12     }
    13     catch(Exception& e)
    14     {
    15         cout << "catch(Exception& e)" << endl;
    16         cout << e.message() << endl;
    17         cout << e.location() << endl;
    18     }
    19 
    20     return 0;
    21 }

    执行结果如下:

     完善Exception.h中的其它异常类,如下所示:

      1 #ifndef EXCEPTION_H
      2 #define EXCEPTION_H
      3 
      4 namespace DTLib
      5 {
      6 
      7 #define THROW_EXCEPTION(e, m) (throw e(m, __FILE__, __LINE__))
      8 
      9 class Exception
     10 {
     11 protected:
     12     char *m_message;
     13     char *m_location;
     14 
     15     void init(const char* message, const char* file, int line);
     16 public:
     17     Exception(const char* message);
     18     Exception(const char* file, int line);
     19     Exception(const char* message, const char* file, int line);
     20 
     21     Exception(const Exception& e);
     22     Exception& operator= (const Exception& e);
     23 
     24     virtual const char* message() const;
     25     virtual const char* location() const;
     26 
     27     virtual ~Exception() = 0;
     28 };
     29 
     30 class ArithmeticException : public Exception
     31 {
     32 public:
     33     ArithmeticException() : Exception(0, 0, 0) {}
     34     ArithmeticException(const char* message) : Exception(message) {}
     35     ArithmeticException(const char* file, int line) : Exception(file, line) {}
     36     ArithmeticException(const char* message, const char* file, int line) : Exception(message, file, line){}
     37 
     38     ArithmeticException(const ArithmeticException& e) : Exception(e) {}
     39 
     40     ArithmeticException& operator=(const ArithmeticException& e)
     41     {
     42         Exception::operator =(e);
     43 
     44         return *this;
     45     }
     46 };
     47 
     48 class NullPointerException : public Exception
     49 {
     50 public:
     51     NullPointerException() : Exception(0, 0, 0) {}
     52     NullPointerException(const char* message) : Exception(message) {}
     53     NullPointerException(const char* file, int line) : Exception(file, line) {}
     54     NullPointerException(const char* message, const char* file, int line) : Exception(message, file, line){}
     55 
     56     NullPointerException(const NullPointerException& e) : Exception(e) {}
     57 
     58     NullPointerException& operator=(const NullPointerException& e)
     59     {
     60         Exception::operator =(e);
     61 
     62         return *this;
     63     }
     64 };
     65 
     66 class IndexOutOfBoundsException : public Exception
     67 {
     68 public:
     69     IndexOutOfBoundsException() : Exception(0, 0, 0) {}
     70     IndexOutOfBoundsException(const char* message) : Exception(message) {}
     71     IndexOutOfBoundsException(const char* file, int line) : Exception(file, line) {}
     72     IndexOutOfBoundsException(const char* message, const char* file, int line) : Exception(message, file, line){}
     73 
     74     IndexOutOfBoundsException(const IndexOutOfBoundsException& e) : Exception(e) {}
     75 
     76     IndexOutOfBoundsException& operator=(const IndexOutOfBoundsException& e)
     77     {
     78         Exception::operator =(e);
     79 
     80         return *this;
     81     }
     82 };
     83 
     84 class NoEnoughMemoryException : public Exception
     85 {
     86 public:
     87     NoEnoughMemoryException() : Exception(0, 0, 0) {}
     88     NoEnoughMemoryException(const char* message) : Exception(message) {}
     89     NoEnoughMemoryException(const char* file, int line) : Exception(file, line) {}
     90     NoEnoughMemoryException(const char* message, const char* file, int line) : Exception(message, file, line){}
     91 
     92     NoEnoughMemoryException(const NoEnoughMemoryException& e) : Exception(e) {}
     93 
     94     NoEnoughMemoryException& operator=(const NoEnoughMemoryException& e)
     95     {
     96         Exception::operator =(e);
     97 
     98         return *this;
     99     }
    100 };
    101 
    102 class InvalidParameterException : public Exception
    103 {
    104 public:
    105     InvalidParameterException() : Exception(0, 0, 0) {}
    106     InvalidParameterException(const char* message) : Exception(message) {}
    107     InvalidParameterException(const char* file, int line) : Exception(file, line) {}
    108     InvalidParameterException(const char* message, const char* file, int line) : Exception(message, file, line){}
    109 
    110     InvalidParameterException(const InvalidParameterException& e) : Exception(e) {}
    111 
    112     InvalidParameterException& operator=(const InvalidParameterException& e)
    113     {
    114         Exception::operator =(e);
    115 
    116         return *this;
    117     }
    118 };
    119 
    120 }
    121 
    122 #endif // EXCEPTION_H

    Exception.cpp文件和上面一样,再次贴出如下:

     1 #include "Exception.h"
     2 #include <cstring>
     3 #include <cstdlib>
     4 
     5 using namespace std;
     6 
     7 namespace DTLib
     8 {
     9 
    10 void Exception::init(const char *message, const char *file, int line)
    11 {
    12     m_message = strdup(message);
    13 
    14     if(file != NULL)
    15     {
    16         char sl[16] = {0};
    17 
    18         itoa(line, sl, 10);
    19 
    20         m_location = static_cast<char*>(malloc(strlen(file) + strlen(sl) + 2));
    21         m_location = strcpy(m_location, file);
    22         m_location = strcat(m_location, ":");
    23         m_location = strcat(m_location, sl);
    24     }
    25     else
    26     {
    27         m_location = NULL;
    28     }
    29 }
    30 
    31 Exception::Exception(const char *message)
    32 {
    33     init(message, NULL, 0);
    34 }
    35 
    36 Exception::Exception(const char *file, int line)
    37 {
    38     init(NULL, file, line);
    39 }
    40 
    41 Exception::Exception(const char *message, const char *file, int line)
    42 {
    43     init(message, file, line);
    44 }
    45 
    46 Exception::Exception(const Exception &e)
    47 {
    48     m_message = strdup(e.m_message);
    49     m_location = strdup(e.m_location);
    50 }
    51 
    52 Exception& Exception::operator =(const Exception& e)
    53 {
    54     if(this != &e)
    55     {
    56         free(m_message);
    57         free(m_location);
    58 
    59         m_message = strdup(e.m_message);
    60         m_message = strdup(e.m_location);
    61     }
    62 
    63     return *this;
    64 }
    65 
    66 const char* Exception::message() const
    67 {
    68     return m_message;
    69 }
    70 
    71 const char* Exception::location() const
    72 {
    73     return m_location;
    74 }
    75 
    76 Exception::~Exception()
    77 {
    78     free(m_message);
    79     free(m_location);
    80 }
    81 
    82 }

    主函数测试程序如下:

     1 #include <iostream>
     2 #include "Exception.h"
     3 using namespace std;
     4 using namespace DTLib;
     5 
     6 
     7 int main()
     8 {
     9     try
    10     {
    11         THROW_EXCEPTION(NoEnoughMemoryException, "test");
    12     }
    13     catch(NoEnoughMemoryException& e)
    14     {
    15         cout << "catch(NoEnoughMemoryException& e)" << endl;
    16         cout << e.message() << endl;
    17         cout << e.location() << endl;
    18     }
    19     catch(Exception& e)
    20     {
    21         cout << "catch(Exception& e)" << endl;
    22         cout << e.message() << endl;
    23         cout << e.location() << endl;
    24     }
    25 
    26     return 0;
    27 }

    执行结果如下:

     由以上异常类族可以得出以下设计原则:

      如果要编写可复用的代码库,尽量使用面向对象技术进行架构,尽量使用异常机制分离正常逻辑和异常逻辑。

    小结:

      现代C++库必然包含充要的异常类族

      所有库中的数据结构类都依赖于异常机制,依赖图如下

      异常机制能够分离库中代码的正常逻辑和异常逻辑

  • 相关阅读:
    C# Split() 去除 分组
    C#获取当前路径的7种方法
    给 C# 开发者的代码审查清单
    【知乎】一句话答案收录集,一句足矣
    C# string和byte[]的转换
    WCF 有零个操作;协定必须至少有一个操作
    WPF中动态改变控件显示位置
    转 将iPhone和Android手机屏幕投影仪投影显示
    转 MySQL数据库监控软件lepus使用问题以及解决办法
    转 Shell中的IFS解惑
  • 原文地址:https://www.cnblogs.com/wanmeishenghuo/p/9499647.html
Copyright © 2020-2023  润新知