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

一、自定义异常类

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)、异常机制能够分离库中代码的正常逻辑和异常逻辑

时间: 2024-10-16 12:12:31

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

第九课、智能指针示例------狄泰软件学院

一.内存泄漏(臭名昭著的bug) (1).动态申请堆空间,用完后不归还 (2).c++语言中没有垃圾回收机制 (3).指针无法控制所指向的堆空间生命周期(如局部指针生命周期结束了堆空间的生命周期还未结束) 二.智能指针 1.当代c++平台的智能指针 (1).指针生命周期结束时主动释放堆空间 (2).一片堆空间最多只能由一个智能指针标识 (3).杜绝指针运算和指针比较 2.智能指针的设计方案 (1).通过类模板描述指针的行为:能够定义不同类型的指针变量 (2).重载指针特征操作符(->和*):利用

数据结构第六课------------狄泰软件学院

图片出自狄泰软件学院工程里面一般采用时间复杂度在 O(n^3) 之前的算法.算法的最好与最坏情况:一般推断算法的最坏情况,当程序在最坏情况都能满足要求是,说明在最好和平均情况都能满足要求. 算法的空间复杂度 (Space Complexity)定义:S(n) = S(f(n))n为算法的问题规模f(n)为空间使用函数,与n有关推导时间复杂度的方法同样适用于空间复杂度如当算法所需要的空间复杂度是常数时,空间复杂度为S(1)S(n+4) = S(n)空间与时间的比较(1)多数情况下,算法的时间复杂度

第十三课、类族的结构进化-------------------狄泰软件学院

一.遵循经典设计原则 DTLib中的所有类位于单一继承树 IvalidOperationEXception是新增的异常类,描述某些成员函数在状态不对的时候被抛出 二.改进的关键点 1.EXception类继承自Object类:堆空间中创建异常对象失败时,返回NULL指针,用的是自己的new版本 更改: 2.新增IvalidOperationEXception异常类:成员函数调用时,如果状态不正确则抛出该异常 增加: //非法操作异常 class IvalidOperationEXception:

异常类的构建(四)

我们在之前学习了 C++ 中有关异常的知识,现在我们来重新回顾下.那么异常的格式是什么呢?便是 try ... catch ...:try 语句处理正常的代码逻辑,而 catch 语句则处理异常情况,try 语句中的异常由对应的 catch 语句处理.格式如下 try {     double r = divide(1, 0); } catch(...) {     cout << "Divided by zero ..." << endl; } 在 C++ 中

异常类的构建

1.Exception.h 中增加ArithmetricException类 class ArithmetricException:public Exception { public: ArithmetricException():Exception(0) {} //我认为这个实现没有必要 ArithmetricException(const char* message):Exception(message) {} ArithmetricException(const char* file, i

快学Scala 第十一课 (类继承)

类继承: class People { } class Emp extends People{ } 和Java一样,final的类不能被继承.final的字段和方法不能被override. 在Scala中重写一个非抽象方法必须使用override, 继承抽象方法前面加了override也没关系. abstract class Person { def say(s: String): Unit } class Worker extends Person{ override def say(s: S

第五十八课、自定义模型类(上)------------------狄泰软件学院

 一.自定义模型类 1.QStandardItemModel是一个通用的模型类 (1).能够以任意的方式组织数据(线程.非线性) (2).数据组织的基本单位为数据项(QStandardItem) (3).每一个数据项能够存储多个具体数据(附加数据角色) (4).每一个数据项能够对数据状态进行控制(可编辑.可选...) 2.Qt中的通用模型类QStandardItemModel (1).QStandardItemModel继承自抽象的模型类QAbstractItemModel (2).QStand

java-web——第十一课 时间类

前言: 在我们学习java web之中,实体类的属性经常会遇到时间类 时间类也是一个很重要的类, 首先我们先了解一下经常使用的时间类 经常使用的时间类也就是这三个类 具体的可以查阅api http://tool.oschina.net/apidocs/apidoc?api=jdk_7u4 我们这章主要是做三个题目来熟悉一下这三个类的作用 题目: 题目一:计算今天距离本年的国庆(2017-10-1)有多少天? 题目二:计算上上周星期五的日期是? 题目三:计算去年国庆节的第五天是星期几? 解决问题一

第十四课、线性表的本质和操作-----------------狄泰软件学院

一.线性表(List)的表现形式 1.零个或者多个数据元素组成的集合 2.数据元素在位置上是有序排列的 3.数据元素的个数是有限的 4.数据元素的类型必须相同 二.线性表(List)的抽象定义 1.线性表是具有相同类型的n(>=0)个数据元素的有限序列 {a0,a1,a2,....an-1} ai是表项(数据元素),n是表长度 三.线性表(List)的性质 1.a0为线性表的第一个元素,只有一个后继 2.an-1为线性表的最后一个元素,只有一个前驱 3.除a0和an-1外的其它元素ai,既有前驱