亲宝软件园·资讯

展开

C++异常系统

阿莫·西林 人气:0

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

结果:

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

加载全部内容

相关教程
猜你喜欢
用户评论