第十一课、异常类的构建-------------狄泰软件学院

一、自定义异常类

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

2、对于类类型的匹配依旧是之上而下的严格匹配

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

所以要

(1)、匹配子类异常的catch放在上部

(2)、匹配父类异常的catch放在下部

4、异常类是数据结构所依赖的“基础设施”(现代c++库也必然包含充要的异常类族)

二、一步步打造自己的异常类

1、首先是抽象类EXception的编写,既然是抽象类,必然含有纯虚函数,通常的做法都是将析构函数作为纯虚函数

头文件:接口定义

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

实现文件:

void Exception::init(const char* message, const char* file, int line)
{
    m_message = strdup(message);//指向的message可能在栈空间,也可能在堆空间或者全局数据去,
                                //为了安全,这里先复制一份到堆空间

    if( file != NULL)
    {
        char sl[16] = {0};
        itoa(line, sl, 10);//首先将行号转化为字符串

        m_location = static_cast<char*>(malloc(strlen(file) + strlen(sl) + 2));
        m_location = strcat(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);
}

          由于构造函数的几个重载实现方式都差不多,所以定义一个init()函数来进行初始化会方便一点。那EXception类的接口函数const char* Exception::message() const和const char* Exception::location() const为什么要定义成const属性的呢?原因很简单,当有人捕捉到异常后,肯定只希望看到异常是什么样,而不希望改变不小心改变相应的成员变量的值,那怎么样才能保证对象的成员变量的值不被改变?那就是将对象用const修饰使其成为只读对象,而const对象只能调用const的成员函数,这就是为什么将这两个函数声明为const的原因。后面做测试的时候我们就将看到const对象调用const函数。

          可能还会有疑问的一点是析构函数不是纯虚函数吗?纯虚函数不是只声明而不定义?这是c++的语法规定,由于子类对象在析构时也必须调用父类的析构函数,故即使父类的析构函数定义为纯虚函数也应该实现它。

2、各种异常类的实现:继承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;
    }
};

3、至此,我们的异常类的构建已全部完成,接下来是测试阶段

为了使用方便,我们可以首先定义一个宏,以后使用时使用该宏只需要填上相应的类名和抛出的信息即可

#define THROW_EXCEPTION(e,m)  (throw e(m, __FILE__, __LINE__))

测试代码:

int main()
{
    try
    {
        THROW_EXCEPTION(IndexOutOfBoundsException, "test");
    }
    catch(const IndexOutOfBoundsException& e)
    {
        cout << "ArithmeticException" << endl;
        cout << e.message() << endl;
        cout << e.location() << endl;
    }
    catch(const Exception& e)
    {
        cout << "Exception" << endl;
        cout << e.message() << endl;
        cout << e.location() << endl;
    }
    return 0;
}

一定要注意匹配子类异常的catch放在上部,匹配父类异常的catch放在下部。

三、完整代码

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

}

#endif // EXCEPTION_H
EXception.h
#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 = strdup(message);//指向的message可能在栈空间,也可能在堆空间或者全局数据去,
                                //为了安全,这里先复制一份到堆空间

    if( file != NULL)
    {
        char sl[16] = {0};
        itoa(line, sl, 10);//首先将行号转化为字符串

        m_location = static_cast<char*>(malloc(strlen(file) + strlen(sl) + 2));
        m_location = strcat(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.cpp
#include <iostream>
#include "SmartPointer.h"
#include "Exception.h"

using namespace std;
using namespace DTLib;

int main()
{
    try
    {
        THROW_EXCEPTION(IndexOutOfBoundsException, "test");
    }
    catch(const IndexOutOfBoundsException& e)
    {
        cout << "ArithmeticException" << endl;
        cout << e.message() << endl;
        cout << e.location() << endl;
    }
    catch(const Exception& e)
    {
        cout << "Exception" << endl;
        cout << e.message() << endl;
        cout << e.location() << endl;
    }
    return 0;
}
main.cpp

四、小结

(1)、现代c++库必然包含充要的异常类族

(2)、所有库中的数据结构都依赖于异常机制

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

 

posted @ 2017-05-08 18:04  lgc202  阅读(639)  评论(0编辑  收藏  举报