11_异常类构建

关键词: 异常类:计算异常、空指针异常、越界异常、内存不足异常、参数错误异常; 可复用代码库设计原则;数据结构依赖异常类

写在前面的话: 前面学习到的异常类都是抛出的基本类型的异常类,是否也可以抛出类类型的异常?也就是说异常的类型是否可以支持自定义类类型?这节课重点讲解异常类的构建和使用。

1. 异常的类型

  • 异常的类型可以是自定义类类型
  • 对于类类型异常的匹配依旧是至上而下严格匹配
  • 赋值兼容性原则在异常匹配中依然适用
  • 一般而言:匹配子类异常的catch放在上部,匹配父类异常的catch放下下部

2. 现代C++库包含的异常类族

异常类是其它数据结构类所依赖的“基础设施”

《11_异常类构建》 C++库中包含的异常类

由上面类图可知:

Exception为抽象类,是其他异常类的父类,
它不能定义对象

《11_异常类构建》 异常类功能定义

3. 异常类中的接口定义

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();
};

编程说明:创建异常类族
Exception.h

#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();
};

class ArithmeticException : public Exception
{
public:
    ArithmeticException() : Exception(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;
    }

};

class NullPointerException : public Exception
{
public:
    NullPointerException() : Exception(0){}
    NullPointerException(const char* message) : Exception(message){}
    NullPointerException(const char* file, int line) : Exception(file, line){}
    NullPointerException(const char* message, const char* file, int line) : Exception(message, file, line){}

    NullPointerException(const NullPointerException& e) : Exception(e){}

    NullPointerException& operator =(const NullPointerException& e)
    {
        Exception::operator=(e);

        return *this;
    }
};

class IndexOutOfBoundsException : public Exception
{
public:
    IndexOutOfBoundsException() : Exception(0){}
    IndexOutOfBoundsException(const char* message) : Exception(message){}
    IndexOutOfBoundsException(const char* file, int line) : Exception(file, line){}
    IndexOutOfBoundsException(const char* message, const char* file, int line) : Exception(message, file, line){}

    IndexOutOfBoundsException(const IndexOutOfBoundsException& e) : Exception(e){}

    IndexOutOfBoundsException& operator =(const IndexOutOfBoundsException& e)
    {
        Exception::operator=(e);

        return *this;
    }
};

class NoEnoughMemoryExcetion : public Exception
{
public:
    NoEnoughMemoryExcetion() : Exception(0){}
    NoEnoughMemoryExcetion(const char* message) : Exception(message){}
    NoEnoughMemoryExcetion(const char* file, int line) : Exception(file, line){}
    NoEnoughMemoryExcetion(const char* message, const char* file, int line) : Exception(message, file, line){}

    NoEnoughMemoryExcetion(const NoEnoughMemoryExcetion& e) : Exception(e){}

    NoEnoughMemoryExcetion& operator =(const NoEnoughMemoryExcetion& e)
    {
        Exception::operator=(e);

        return *this;
    }
};

class InvalidParameterExcetion : public Exception
{
public:
    InvalidParameterExcetion() : Exception(0){}
    InvalidParameterExcetion(const char* message) : Exception(message){}
    InvalidParameterExcetion(const char* file, int line) : Exception(file, line){}
    InvalidParameterExcetion(const char* message, const char* file, int line) : Exception(message, file, line){}

    InvalidParameterExcetion(const InvalidParameterExcetion& e) : Exception(e){}

    InvalidParameterExcetion& operator =(const InvalidParameterExcetion& e)
    {
        Exception::operator=(e);

        return *this;
    }
};

}

#endif // EXCEPTION_H

Exception.cpp

#include "Exception.h"
#include <cstring>
#include <cstdlib>
#include <cstdio>

using namespace std;

namespace DTLib
{

void Exception::init(const char* message, const char* file, int line)
{
    /*
     * m_message = message; // 这样赋值是不安全的,参数 message 指向的字符串位置不缺定,
     *                      // 可能位于栈空间、可能位于堆空间、也可能位于静态存储区。
     *                      // 解决方案:使用strdup函数,在堆空间中申请内存,拷贝字符串。
     */
    m_message = strdup(message);

    if( file != NULL )      // 首先判断file是否为空, 如果file不为空,
    {                       // file 指向的是发生异常的文件名,
        char sl[16] = {0};  // 接下来,将发生异常的行号转化为字符串。定义一个辅助的字符数组

        sprintf(sl, "%d", line); // 将line从int转换为字符数组,并且存储于sl中。
                                 // 在Linux环境下没有itoa函数,因此需要用sprintf函数替代,并包含<cstdio>头文件
                                 // 接下来将异常的文件名和行号拼接
                                 // 通过malloc申请一片内存空间,大小为strlen(file)m+strlen(ls)+2
        m_location = static_cast<char*>(malloc(strlen(file) + strlen(sl) + 2)); // +2 的目的是为了保存":"和"\n"
        m_location = strcpy(m_location, file);  // 将file中的字符串拷贝到m_location中
        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);
}

}

测试代码:

#include <iostream>

#include "SmartPointer.h"
#include "Exception.h"

using namespace std;
using namespace DTLib;

int main()
{
    try
    {
        THROW_EXCEPTION(InvalidParameterExcetion, "test");
    }
    catch(const InvalidParameterExcetion& e)
    {
        cout << "catch(const InvalidParameterExcetion& e)" << endl;
        cout << e.message() << endl;
        cout << e.location() << endl;
    }
    catch(const NoEnoughMemoryExcetion& e)
    {
        cout << "catch(const NoEnoughMemoryExcetion& e)" << endl;
        cout << e.message() << endl;
        cout << e.location() << endl;
    }
    catch(const IndexOutOfBoundsException& e)
    {
        cout << "catch(const IndexOutOfBoundsException& e)" << endl;
        cout << e.message() << endl;
        cout << e.location() << endl;
    }
    catch(const NullPointerException& e)
    {
        cout << "catch(const NullPointerException& e)" << endl;
        cout << e.message() << endl;
        cout << e.location() << endl;
    }
    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;
}

4. 设计原则

在可复用代码库设计时,尽量使用面向对象技术进行架构,尽量使用异常处理机制分离正常逻辑和异常逻辑。

5. 小结

  • 现代C++库必然包含必要的异常类族
  • 所有库中的数据结构类都依赖于异常机制
  • 异常机制能够分离库中代码的正常逻辑异常逻辑
    《11_异常类构建》 数据结构类依赖异常类

声明:此文章仅是本人在学习狄泰学院《数据结构实战开发教程》所做的笔记,文章中包含狄泰软件资料内容,一切版权归狄泰软件所有!
实验环境:ubuntu10 + Qt Creator2.4.1 + Qt SDK 4.7.4

    原文作者:jacob2359
    原文地址: https://www.jianshu.com/p/4066e45d7d18
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞