数据结构第11课 异常类的构建

本文学习自 狄泰软件学院 唐佐林老师的 数据结构课程


经验:依赖于异常特性所创建出来的库是非常稳定的!

问:我们自己创建的数据结构库是不是也可以引进异常类族呢?
答:可以

有基本类型的异常(int char …等等),当然也可以抛出异常类的对象。

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

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

如何设计一个异常类族?
由于我们的目的是创建一个数据结构库,针对数据结构中的代码,我们可以这样设计:
在这里插入图片描述

  • Exception(顶层父抽象类,不能够创建对象,用来被继承,创建以下五个异常类)

    根据经验,在编写数据结构库的时候,一般而言就会出现这几种异常出来,因此对应我们也创建这五个异常

  • ArithmeticException 计算异常

  • NullPointException 空指针异常

  • IndexOutOfBoundsException 越界异常

  • NoEnoughMemoryException 内存不足异常

  • InvalidParameterException 参数错误异常

在这里插入图片描述
实验1 : 创建一个异常类组,作为数据库的异常类的顶层父类,并验证

exception.h

#ifndef EXCEPTION_H
#defined 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);
	
	//拷贝构造函数,赋值操作符重载(init函数中 涉及到堆空间的操作,则拷贝操作和赋值操作都是深拷贝)
	//拷贝构造函数,赋值操作符重载 要保证每一个Exception对象内部的两个成员指针所执行的内容都应该是独立的一段堆空间,(防止多次free bug)
	Exception(const Exception& e);
	Exception& operator=(const Exception& e);
	
	//返回对应的成员指针
	virtual const char* message() const;
	virtual const char* location() const;
	
	//虚析构函数 = 0,意味着这是一个纯虚的析构函数,纯虚的析构函数作用只有一个:说明当前这个类是一个抽象类
	//抽象类不能创建对象,Exception 类顶层的抽象父类,不能创建对象,应该有子类创建对象,但是测试Exception 类时 需要先写成virtual ~Exception();
	//由子类创建,此时初步程序还没有实现子类,所以暂时 将纯虚构特征去掉,实验一下
	virtual ~Exception() =0;
};

}

#endif

exception.cpp

#include "Exception"
#include <cstring>  //字符串类头文件

using namespace std;//

namespace DTLib
{
	
void Exception::init(const char* message, const char* file, int line)
{
	//m_message = message; //不能这样直接赋值,因为参数message指针指向的字符串位置不确定,有可能在堆空间、栈空间、全局数据区,所以没有办法控制
						   //message 所指向的外部字符串的生命周期,所以这样是不安全的,正确的做法是拷贝一份字符串出来。
	m_message = strdup(message);//拷贝一份字符串到堆空间,所以此时m_message 指向堆空间
	
	if(file != NULL)
	{
		char sl[16] = {0};//存储异常文件信息行号
		itoa(line,sl,10);//将line 从整形转换为字符串 并存储‘’
		m_location = static_cast<char*>(malloc(strlen(file) + strlen(sl) + 2));//申请空间用于存储 拼接file 和 line所对应的字符串,2用于存储 “:”和 字符串借宿符\0
		m_location = strcpy(m_location,file);//字符串拷贝
		m_location = strcat(m_location,":");//字符串拼接,首先拼接一个":"
		m_location = strcat(m_location,sl); //形成 :  文件名:行号  的异常信息格式
	}
	else
	{
		m_message = NULL;
	}
}
	
//重载构造函数
Exception::Exception(const char* message)
{
	init(const char* message,NULL,0);
}
	
Exception::Exception(const char* file, int line)
{
	init(NULL,const char* file, int line);
}
	
Exception::Exception(const char* message, const char* file, int line)
{
	init(const char* message, const char* file, int line);
}

//拷贝构造函数
Exception::Exception(const Exception& e)
{
	m_location = strdup(e.m_location);//拷贝一份字符串到堆空间,所以此时m_location指向堆空间
	m_message = strdup(e.m_message);//拷贝一份字符串到堆空间,所以此时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
	{
		//扔出一个异常:此处扔出的异常是 Exception类的一个对象
		//throw Exception("test", __FILE__, __line__);
		THROW_EXCEPTION(Exception, "test")
	}
	catch(const Exception& e)
	{
		cout<< "catch(const Exception& e)" <<endl;
		cout<< e.message() <<endl;
		cout<< e.localtion() <<endl;
	}
	return 0;
}

catch(const Exception& e
test
…\DTLib\main.cpp:12


实验2 :添加子类

exception.h

#ifndef EXCEPTION_H
#defined 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);
	
	//拷贝构造函数,赋值操作符重载(init函数中 设计到堆空间的操作,则拷贝操作和赋值操作都是深拷贝)
	//拷贝构造函数,赋值操作符重载 要保证每一个Exception对象内部的两个成员指针所执行的内容都应该是独立的一段堆空间,(防止多次free bug)
	Exception(const Exception& e);
	Exception& operator=(const Exception& e);
	
	//返回对应的成员指针
	virtual const char* message() const;
	virtual const char* location() const;
	
	//虚析构函数 = 0,意味着这是一个纯虚的析构函数,纯虚的析构函数作用只有一个:说明当前这个类是一个抽象类
	//抽象类不能创建对象,Exception 类顶层的抽象父类,不能创建对象,应该有子类创建对象,但是测试Exception 类时 需要先写成virtual ~Exception();
	//由子类穿件,此时初步程序还没有实现子类,所以暂时 将纯虚构特征去掉,实验一下
	virtual ~Exception() =0;
};

//计算异常子类
class AirthmeticException : public Exception
{
	public:
	AirthmeticException() : Exception(0) { };
	AirthmeticException(const char* message) : Exception(const char* message) { };
	AirthmeticException(const char* file, int line) :Exception(const char* file, int line) { };
	AirthmeticException(const char* message, const char* file, int line) : Exception(const char* message, const char* file, int line) { };
	
	AirthmeticException(const Exception& e) : Exception(e) { };
	AirthmeticException& operator=(const AirthmeticException& e)
	{
		Exception :: operator(e);
		return *this;
	}
};
	
}

#endif

exception.cpp 不变

main.cpp

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

using namespace std;
using namespace DTLib;

int main()
{
	try
	{
		//扔出一个异常:此处扔出的异常是 Exception类的一个对象
		//throw Exception("test", __FILE__, __line__);
		THROW_EXCEPTION(AirthmeticException, "test")
	}
	catch(const Exception& e)
	{
		cout<< "catch(const Exception& e)" <<endl;
		cout<< e.message() <<endl;
		cout<< e.localtion() <<endl;
	}
	catch(const AirthmeticException & e)
	{
		cout<< "catch(const AirthmeticException & e)" <<endl;
		cout<< e.message() <<endl;
		cout<< e.localtion() <<endl;
	}
	return 0;
}

结果:
catch(const Exception& e)
test
…\DTLib\main.cpp:12

实验说明:
异常的匹配是自上而下的并且只匹配一次,如果catch语句块所捕获的异常类型是具有继承关系的,那么父类所对应的catch语句放在下面,子类所对应的catch语句快放在上面; 所以需要将 catch(const AirthmeticException & e) 放在 catch(const Exception& e) 之前;


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

小结:

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Linux老A

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值