11.异常类构建

目录

编程实验:创建异常类族

设计原则

小结


编程实验:创建异常类族

#ifndef EXCEPTION_H
#define EXCEPTION_H

#include "object.h"
//#define EXCEPTION_TEST
//#define EXCEPTION_TEST_PARENT_CLASS
#define EXCEPTION_TEST_CHILD_CLASS
namespace JQLib
{
//父类
#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);
    virtual const char* message() const;
    virtual const char* location() const;
    //注意:
        //(1)析构函数是较为特殊的函数,一旦定义了析构函数,不管这个函数是不是纯虚函数,就
        //必须提供实现。因为,对象在销毁时,最后都会调用父类的析构函数。如果父类不提供实现,
        //当对象销毁过程中调用到父类析构函数时,就找不到析构函数,也就不知该如何析构下去。
        //因此,尽管这里将析构函数声明为纯虚函数,但Exception类仍提供析构函数的实现。以便
        //最后正确释放掉m_message和m_location所指的堆空间.
        //(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;
    }
};

//空指针异常类
//Index out of bounds exception
class IndexOutOfBoundsExcpt: public Exception{
public:
    IndexOutOfBoundsExcpt():Exception(0){};
    IndexOutOfBoundsExcpt(const char* message):Exception(message){};
    IndexOutOfBoundsExcpt(const char* file, int line):Exception(file, line){};
    IndexOutOfBoundsExcpt(const char* message, const char* file, int line):Exception(message, file, line){};

    IndexOutOfBoundsExcpt(const IndexOutOfBoundsExcpt& ae):Exception(ae){};
    IndexOutOfBoundsExcpt& operator = (const IndexOutOfBoundsExcpt& ae){ return (*this = ae);};

};
//越界异常类
//No enough memory exception
class NoEnoughMemoryExcpt: public Exception{
public:
    NoEnoughMemoryExcpt():Exception(0){};
    NoEnoughMemoryExcpt(const char* message):Exception(message){};
    NoEnoughMemoryExcpt(const char* file, int line):Exception(file, line){};
    NoEnoughMemoryExcpt(const char* message, const char* file, int line):Exception(message, file, line){};

    NoEnoughMemoryExcpt(const NoEnoughMemoryExcpt& ae):Exception(ae){};
    NoEnoughMemoryExcpt& operator = (const NoEnoughMemoryExcpt& ae){ return (*this = ae);};

};
//内存不足异常类
//Null point exception
class NullPointExcpt: public Exception{
public:
    NullPointExcpt():Exception(0){};
    NullPointExcpt(const char* message):Exception(message){};
    NullPointExcpt(const char* file, int line):Exception(file, line){};
    NullPointExcpt(const char* message, const char* file, int line):Exception(message, file, line){};

    NullPointExcpt(const NullPointExcpt& ae):Exception(ae){};
    NullPointExcpt& operator = (const NullPointExcpt& ae){ return (*this = ae);};

};
//参数错误异常类
//Invalid Parameter exception
class InvalidParameterExcpt: public Exception{
public:
    InvalidParameterExcpt():Exception(0){};
    InvalidParameterExcpt(const char* message):Exception(message){};
    InvalidParameterExcpt(const char* file, int line):Exception(file, line){};
    InvalidParameterExcpt(const char* message, const char* file, int line):Exception(message, file, line){};

    InvalidParameterExcpt(const InvalidParameterExcpt& ae):Exception(ae){};
    InvalidParameterExcpt& operator = (const InvalidParameterExcpt& ae){ return (*this = ae);};

};

}
#endif // EXCEPTION_H

Exception.cpp
#include "Exception.h"
#include <cstring>  //字符串相关头文件
#include <cstdlib>
using namespace std;
namespace JQLib
{
void Exception::init(const char* message, const char* file, int line)
{
    //直接这样复制不可取,message这个指针可能在栈上、堆空间、全局数据区
    //这样没办法控制message的生命周期,所以这样是不安全的
    //m_message = message;
    //所以可以拷贝一份字符串出来,先包含头文件
    //复制到堆空间,然后m_message指向堆空间
    m_message = strdup(message);
    if(file != NULL)
    {
        //将行号转换成字符串
        //定义一个辅助的数组
        char sl[16] = {0};
        itoa(line, sl, 10);
        //+2:一个用来存冒号,一个用来存结束符
        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_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);
}
}
main.cpp
#include <iostream>
#include "SmartPointer.h"
#include "Exception.h"
using namespace std;
using namespace JQLib;
#ifdef EXCEPTION_TEST
int main()
{
#ifdef EXCEPTION_TEST_CHILD_CLASS
    try
    {
        THROW_EXCEPTION(ArithmeticException, "test");
    }
    //子类异常放在上部
    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;
    }
#endif
#ifdef EXCEPTION_TEST_PARENT_CLASS
    //这里直接用父类进行测试可以将virtual ~Exception() = 0;
    //改为virtual ~Exception();让编译器编译通过
    try
    {
        THROW_EXCEPTION(Exception, "test");
    }
    catch(const Exception& e)
    {
        cout<<"catch(const Exception& e)"<<endl;
        cout<<e.message()<<endl;
        cout<<e.location()<<endl;
    }
#endif
    return 0;
}
#endif

设计原则

 

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

 

小结

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

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值