异常类的构建——顶层父类Exception的实现

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

 

现代C++库必然包含充要的异常类族
异常类是数据结构类所依赖的基础设施

 

 

Exception是一个抽象类,不能定义对象,用来被继承。通过继承的方式,定义了5个异常类。
ArithmetricException:计算异常。例如1/0的错误
NullPointerException:空指针异常
IndexOutOfBoundsException:越界异常。访问数组的时候,有可能越界
NoEnoughMemoryException:内存不足异常。动态申请内存空间,没有充足的空间时
InvalidParameterException:参数错误异常。参数是否合法

异常类中的接口定义

class Exception
{
protected:
  char* m_message;
  char* m_location;
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.h

#ifndef EXCEPTION_H
#define EXCEPTION_H

namespace DTLib
{
class Exception
{
protected:
    char* m_location;
    char* m_message;
    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 ; //纯虚析构函数,纯虚函数需要提供实现吗?纯虚函数是不需要提供实现的,等待子类去实现。但是这个地方是个例外,C++规定只要自定义了析构函数,不管
                              //这个析构函数是不是纯虚函数,一定要提供实现。为什么?在析构一个对象的时候,最后肯定会调用到父类的析构函数,
};

}

#endif // EXCEPTION_H

Exception.cpp

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

namespace DTLib
{
void Exception::init(const char *message, const char *file, int line)
{
    //m_message = message; //直接进行这样的赋值,这样写可以吗?肯定不行,因为参数的message这个指针它指向的字符串有可能在栈上,有可能在堆空间中,还有可能在全局数据区。
                         //这样的话,没有办法控制message所指向的外部字符串的生命周期,所以说这样的写法是不安全的。可以拷贝一份字符串出来。
    m_message = strdup(message);  //将字符串复制到了堆空间。

    if(file != NULL)
    {
        char sl[16] = {0};
        itoa(line,sl,10);   //itoa在linux下是无法使用的,需要使用sprintf

        //m_location = malloc(strlen(file) + strlen(sl) + 2);  //malloc函数返回的是void*,将void*直接赋值给char*是不可以的。因此需要进行强制类型转换。使用static_cast
        m_location = static_cast<char*>(malloc(strlen(file) + strlen(sl) + 2));
        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_location = strdup(e.m_location);
    m_message = strdup(e.m_message);
}
Exception& Exception::operator= (const Exception& e)
{
    if(this != &e)
    {
        free(m_location);
        free(m_message);
        m_location = strdup(e.m_location);
        m_message = strdup(e.m_message);
    }

    return *this;
}

const char* Exception::message() const
{
    return m_message;
}

const char* Exception::location() const
{
    return m_location;
}

Exception::~Exception()
{
    free(m_location);
    free(m_message);
}
}

main.cpp

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

进一步修改,在头文件中

namespace DTLib
{
#define THROW_EXCEPTION(e,m) (throw e(m, __FILE__,__LINE__))

class Exception

在main.cpp中这样使用

 try
    {
        THROW_EXCEPTION(Exception,"test");  //为了测试,还需要将纯虚特性去掉
    }

使用这个宏的意义在于在需要的地方直接扔出相应的异常对象就可以了,具体的文件名和行号就不用去管了。

 

posted @ 2019-12-13 23:41  一代枭雄  阅读(917)  评论(0编辑  收藏  举报