广告位联系
返回顶部
分享到

C++实现并优化异常系统的方法

C语言 来源:互联网 作者:酷站 发布时间:2022-08-01 12:20:13 人浏览
摘要

C++原本的异常系统是这个样子的: 调用what()方法时只返回异常的名称,并没有显示抛出异常的位置和堆栈跟踪,功能上显得少许的贫瘠... 下面这个是我自己实现的改良版的异常处理系

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

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

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

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

 

一、模拟栈展开的过程

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

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

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

定义一个叫做ExceptionStackTrace的类:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

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这个后面会讲到

};

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

1

2

3

4

5

6

7

static ExceptionStackTrace est = 128;

void method(...) {

    est.push(__FUNCSIG__);

    ... // 异常抛出在这里可以被捕捉

    est.pop();

}

main ...

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

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

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

下面是实现代码:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

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" ", m_message_trace[i]);

    }

}

void ExceptionStackTrace::throw_(SuperException except) {

    // 抛出一个异常

    printf("Unhandled exception: %s: %s ", except.exception_name(), except.message());

    this->print_stack_trace();

    exit(-1);

}

 

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

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

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

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

1

2

3

4

5

6

7

8

9

10

11

#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; // 获取异常名称

};

然后是实现部分:

1

2

3

4

5

6

7

8

9

10

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;

}

具体用法:

1

2

3

4

5

6

7

8

int main() {

    est.push(__FUNCSIG__);

    int i = 0;

    scanf_s("%d", &i);

    if (i == 128) est.throw_(SuperException("这是一个异常。"));

    est.pop();

    return 0;

}

当输入128时:

 

三、超级运用

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

#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 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 a = 128;

    a[129] = new char[16];

    est.pop();

    return 0;

}

结果:

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


版权声明 : 本文内容来源于互联网或用户自行发布贡献,该文观点仅代表原作者本人。本站仅提供信息存储空间服务和不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权, 违法违规的内容, 请发送邮件至2530232025#qq.cn(#换@)举报,一经查实,本站将立刻删除。
原文链接 : https://blog.csdn.net/weixin_45061161/article/details/125993499
相关文章
  • C++中类的六大默认成员函数的介绍

    C++中类的六大默认成员函数的介绍
    一、类的默认成员函数 二、构造函数Date(形参列表) 构造函数主要完成初始化对象,相当于C语言阶段写的Init函数。 默认构造函数:无参的构
  • C/C++实现遍历文件夹最全方法总结介绍

    C/C++实现遍历文件夹最全方法总结介绍
    一、filesystem(推荐) 在c++17中,引入了文件系统,使用起来非常方便 在VS中,可以直接在项目属性中调整: 只要是C++17即以上都可 然后头文件
  • C语言实现手写Map(数组+链表+红黑树)的代码

    C语言实现手写Map(数组+链表+红黑树)的代码
    要求 需要准备数组集合(List) 数据结构 需要准备单向链表(Linked) 数据结构 需要准备红黑树(Rbtree)数据结构 需要准备红黑树和链表适配策略
  • MySQL系列教程之使用C语言来连接数据库

    MySQL系列教程之使用C语言来连接数据库
    写在前面 知道了 Java中使用 JDBC编程 来连接数据库了,但是使用 C语言 来连接数据库却总是连接不上去~ 立即安排一波使用 C语言连接 MySQL数
  • 基于C语言实现简单学生成绩管理系统

    基于C语言实现简单学生成绩管理系统
    一、系统主要功能 1、密码登录 2、输入数据 3、查询成绩 4、修改成绩 5、输出所有学生成绩 6、退出系统 二、代码实现 1 2 3 4 5 6 7 8 9 10 11
  • C语言实现共享单车管理系统

    C语言实现共享单车管理系统
    1.功能模块图; 2.各个模块详细的功能描述。 1.登陆:登陆分为用户登陆,管理员登陆以及维修员登录,登陆后不同的用户所执行的操作
  • C++继承与菱形继承的介绍

    C++继承与菱形继承的介绍
    继承的概念和定义 继承机制是面向对象程序设计的一种实现代码复用的重要手段,它允许程序员在保持原有类特性的基础上进行拓展,增加
  • C/C++指针介绍与使用介绍

    C/C++指针介绍与使用介绍
    什么是指针 C/C++语言拥有在程序运行时获得变量的地址和操作地址的能力,这种用来操作地址的特殊类型变量被称作指针。 翻译翻译什么
  • C++进程的创建和进程ID标识介绍
    进程的ID 进程的ID,可称为PID。它是进程的唯一标识,类似于我们的身份证号是唯一标识,因为名字可能会和其他人相同,生日可能会与其他
  • C++分析如何用虚析构与纯虚析构处理内存泄漏

    C++分析如何用虚析构与纯虚析构处理内存泄漏
    一、问题引入 使用多态时,如果有一些子类的成员开辟在堆区,那么在父类执行完毕释放后,没有办法去释放子类的内存,这样会导致内存
  • 本站所有内容来源于互联网或用户自行发布,本站仅提供信息存储空间服务,不拥有版权,不承担法律责任。如有侵犯您的权益,请您联系站长处理!
  • Copyright © 2017-2022 F11.CN All Rights Reserved. F11站长开发者网 版权所有 | 苏ICP备2022031554号-1 | 51LA统计