1 异常类构建

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

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

2 继承关系图和接口实现

异常类继承关系图
Exception

异常类功能定义

异常类 功能定义
ArithmeticException 计算异常
NullPointerException 空指针异常
IndexOutOfBoundsException 越界异常
NoEnoughMemoryException 内存不足异常
InvalidParameterException 参数错误异常
InvalidOperationException 不合法操作异常(成员函数调用时,如果状态不正确则抛出异常)

异常类中的接口定义

class Exception : public Object
{
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;
};

3 代码实现

Exception.h

#ifndef EXCEPTION_H
#define EXCEPTION_H

#include "Object.h"

namespace LemonLib
{

/*
 * 这里直接抛异常对象即可,不要使用new创建异常对象
 */
#define THROW_EXCEPTION(e, m) (throw e(m, __FILE__, __LINE__))

class Exception : public Object
{
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);

    /*
     * 设计为虚函数的原因:方便异常捕获时,直接用父类型进行匹配,调用该函数时可以进行动态类型识别。
     * const函数:可能会被const对象调用,因此需要加上const。这里应该说必须加,因为对异常进行捕获时,
     * 我们通常会写出类似的代码:catch (const Exception& e)。
     */
    virtual const char* message() const;
    virtual const char* location() const;

    /* 1.成为抽象类。2.使用父类指针释放子类对象时,可以调用子类的析构函数。 */
    virtual ~Exception() = 0;
};

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 NoEnoughMemoryException : public Exception
{
public:
    NoEnoughMemoryException() : Exception(0){}
    NoEnoughMemoryException(const char* message) : Exception(message){}
    NoEnoughMemoryException(const char* file, int line) : Exception(file, line){}
    NoEnoughMemoryException(const char* message, const char* file, int line) : Exception(message, file, line){}
    NoEnoughMemoryException(const NoEnoughMemoryException& e) : Exception(e){}

    NoEnoughMemoryException& operator = (const NoEnoughMemoryException& e)
    {
        Exception::operator =(e);
        return *this;
    }
};


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

    InvalidParameterException& operator = (const InvalidParameterException& e)
    {
        Exception::operator =(e);
        return *this;
    }
};

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

    InvalidOperationException& operator = (const InvalidOperationException& e)
    {
        Exception::operator =(e);
        return *this;
    }
};
}

#endif // EXCEPTION_H

Exception.cpp

#include "Exception.h"

#include <cstring>
#include <cstdlib>

namespace LemonLib
{
    void Exception::init(const char* message, const char* file, int line)
    {
        /* 为了保证message所指向空间的生命周期和异常对象相同,这里我们需要重新开辟空间 */
        m_message = message ? strdup(message) : NULL;

        if (file != NULL)
        {
            char str_line[16] = {0};
            itoa(line, str_line, 10);   /* 10的含义为按10进制进行转换 */
            m_location = static_cast<char*>(malloc(strlen(file) + strlen(str_line) + 2));

            if (m_location != NULL)
            {
                strcpy(m_location, file);   /* 结尾会复制\0 */
                strcat(m_location, ":");    /* 结尾会复制\0 */
                strcat(m_location, str_line);
            }
        }
        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 = e.m_message ? strdup(e.m_message) : NULL;
        m_location = e.m_location ? strdup(e.m_location) : NULL;
    }

    Exception& Exception::operator = (const Exception& e)
    {
        if (this != &e)  /* 防止自赋值 */
        {
            free(m_message);
            free(m_location);

            m_message = e.m_message ? strdup(e.m_message) : NULL;
            m_location = e.m_location ? strdup(e.m_location) : NULL;
        }

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

测试代码main.cpp

#include <iostream>
#include "Object.h"
#include "Exception.h"

using namespace std;
using namespace LemonLib;



int main()
{
    try
    {
        THROW_EXCEPTION(NoEnoughMemoryException, "hello, this is Exception test...");
    }
    catch (const NoEnoughMemoryException& e)
    {
        cout << "catch (const NoEnoughMemoryException& 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;
}

相关文章: