实现C++异常系统的完善

C++原本的异常系统是这个样子的:

 调用what()方法时只返回异常的名称,并没有显示抛出异常的位置和堆栈跟踪,功能上显得少许的贫瘠...

下面这个是我自己实现的改良版的异常处理系统: 

 可以看到详细的信息,下面是实现过程。

一、模拟栈展开的过程

网上看到别人用一些很奇怪的方法来获取堆栈信息,从而实现堆栈跟踪。

个人觉得很费劲,而且还要安装第三方库。

于是我们可以写一个类来模拟这个过程。

定义一个叫做ExceptionStackTrace的类:

class ExceptionStackTrace {
private:
	const char** m_message_trace = nullptr; // 方法名数组
	size_t* m_line_trace = nullptr; // 行数数组
	int m_top; // 栈顶
    int m_size; // 大小

public:
	ExceptionStackTrace(int size);

	void push(const char* message); // 入栈一个方法
	void pop(); // 出栈一个方法
	bool empty() const; // 判断是否为空
	int top() const; // 返回栈顶索引
    int size() const; // 返回大小

	void print_stack_trace() const; // 打印堆栈跟踪信息

	void throw_(SuperException except); // 抛出一个异常,需要继承SuperException这个后面会讲到
};

既然是模拟,所以需要在程序最前面定义一个静态的对象,使用时在每一个函数的开始和结束部分加上这两句:

static ExceptionStackTrace est = 128;

void method(...) {
	est.push(__FUNCSIG__);
    ... // 异常抛出在这里可以被捕捉
	est.pop();
}

main ...

当调用方法时,会在调用ExceptionStackTrace的push方法,将方法信息压栈。

之后再执行方法内部的语句。

最后在将方法出栈,模拟方法已被调用完毕。

下面是实现代码:

ExceptionStackTrace::ExceptionStackTrace(int size) {
    // 尺寸不能是负数
	if (size <= 0) throw std::exception("Size should greater than 0.");
	m_message_trace = new const char*[size];
	m_top = 0;
	m_size = size;
}

void ExceptionStackTrace::push(const char* message) {
    // 方法信息压栈
	m_message_trace[m_top] = message;
	++m_top;
}

void ExceptionStackTrace::pop() {
    // 方法信息出栈,栈空抛异常
	if (this->empty()) throw std::exception("Exception stack trace empty!");
	--m_top;
}

bool ExceptionStackTrace::empty() const {
	return m_top == 0;
}

int ExceptionStackTrace::top() const {
	return m_top;
}

int ExceptionStackTrace::size() const {
	return m_size;
}

void ExceptionStackTrace::print_stack_trace() const {
    // 从后往前,因为栈的性质后进先出
	for (int i = m_top - 1; i >= 0; --i) {
		printf("   At method \"%s\"\n", m_message_trace[i]);
	}
}

void ExceptionStackTrace::throw_(SuperException except) {
    // 抛出一个异常
	printf("Unhandled exception: %s: %s\n", except.exception_name(), except.message());
	this->print_stack_trace();
	exit(-1);
}

二、新异常处理系统中异常的定义

异常包括信息和异常名称,同时还需要支持自定义异常。

所以我们可以搞一个用于新异常系统的父类异常,自定义的异常全部继承这个类即可。

父类的名字叫SuperException,下面是类结构:


#define M_GetName(data) #data // 宏定义,获取字符串形式类的名称

class SuperException {
private:
	const char* m_exception_name = nullptr; // 异常名称
	const char* m_message = nullptr; // 异常消息

public:
    // 构造函数
	SuperException(const char* message, const char* exception_name = M_GetName(SuperException));

	const char* message() const; // 获取异常消息
	const char* exception_name() const; // 获取异常名称
};

然后是实现部分:

SuperException::SuperException(const char* message, const char* exception_name) {
	m_message = message;
	m_exception_name = exception_name;
}

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

const char* SuperException::exception_name() const {
	return m_exception_name;
}

具体用法:

int main() {
	est.push(__FUNCSIG__);

	int i = 0;
	scanf_s("%d", &i);
	if (i == 128) est.throw_(SuperException("这是一个异常。"));

	est.pop();
	return 0;
}

当输入128时:

三、超级运用 


#include "ExceptionStackTrace.h"

static ExceptionStackTrace est = 128;

// 自定义一个异常
class IndexOutOfBoundsException : public SuperException {
public:
	IndexOutOfBoundsException(const char* message, const char* exception_name = M_GetName(Exception))
		: SuperException(message, exception_name) {
	}
};
// 自定义一个异常
class BadArrayException : public SuperException {
public:
	BadArrayException(const char* message, const char* exception_name = M_GetName(Exception))
		: SuperException(message, exception_name) {
	}
};

template<typename T> class Array {
private:
	T* m_array;
	size_t m_length;

private:
    // 下标检查
	void index_check(size_t index) {
		est.push(__FUNCSIG__);
		if (index >= m_length) {
			est.throw_(IndexOutOfBoundsException("Index out of bounds."));
		}
		est.pop();
	}

public:
    // 构造器
	Array(size_t length) {
		est.push(__FUNCSIG__);
		m_length = length;
		try {
			m_array = new T[length];
		} catch (std::bad_alloc) {
			est.throw_(BadArrayException("Cannot create a Array object because no space."));
		}
		est.pop();
	}
    // 索引访问
	T& operator[](size_t index) {
		est.push(__FUNCSIG__);
		index_check(index);
		est.pop();
		return m_array[index];
	}
};

int main() {
	est.push(__FUNCSIG__);

	Array<void*> a = 128;
	a[129] = new char[16];

	est.pop();
	return 0;
}

结果:

 为一的遗憾就是没法加上行号文件等提示信息,如果能够实现的话,我将会在下一篇博客中提及。

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值