`

自己设计C++完善的异常处理类(转)

 
阅读更多

如何写一个异常处理类是一个不太容易的事情,最近刚好接触了一些不错的代码,看到了一些技巧,这里和大家分享一下。

一个相对完善的异常处理类(以及附加的一些东西)应该能够处理下面的一些功能:

1) 能够方便的定义异常类的继承树

2) 能够方便的throw、catch,也就是在代码中捕获、处理代码的部分应该更短

3) 能够获取异常出现的源文件的名字、方法的名字、行号

4) 能够获取异常出现的调用栈并且打印出来

由于目前我用的平台是linux,所以里面调用的一些函数也只是在linux下面有用。Windows也肯定是具有相应的函数的,具体可能需要去查查

首先科普一些内容:

1) 对于没有捕获的异常(no handler),则会终止程序,调用terminate()

2) 在定义函数的时候,我们可以在定义的后面加上throw (exception1, exception2…):

a) 如果没有写这一段、则可能抛出任意的异常

b) 如果写throw(),则表示函数不能抛出任意的异常

c) 如果写throw(A, B), 表示函数抛出A、B的异常

如果抛出的异常不在列表范围内,则异常不能被catch,也就会调用terminate()

我们构想一下我们定义、调用我们的异常类的时候是怎样的一个情形:

1) 定义:

1:class DerivedException : public BaseException

2: {

3:public:

4: MY_DEFINE_EXCEPTION(DerivedException, BaseException);

5: };

2) 如何抛出异常

1: MY_THROW(DerivedException)

3) 如何catch异常

1:catch (DerivedException& e)

2: {

3: cout<< e.what() << endl;

4: }

这个输出的内容包括错误的行号、文件名、方法名、和调用栈的列表

给出我们异常类的头文件:

1:#ifndef EXCEPTION_TEST

2:#define EXCEPTION_TEST

3:

4:#include

5:#include

6:

7:#define MY_THROW(ExClass, args...) \

8:do \

9: { \

10: ExClass e(args); \

11: e.Init(__FILE__, __PRETTY_FUNCTION__, __LINE__); \

12:throw e; \

13: } \

14:while (false)

15:

16:#define MY_DEFINE_EXCEPTION(ExClass, Base) \

17: ExClass(const std::string& msg = "") throw() \

18: : Base(msg) \

19: {} \

20: \

21: ~ExClass() throw() {} \

22: \

23:/* override */ std::string GetClassName() const \

24: { \

25:return #ExClass; \

26: }

27:

28:class ExceptionBase : public std::exception

29: {

30:public:

31: ExceptionBase(const std::string& msg = "") throw();

32:

33:virtual ~ExceptionBase() throw();

34:

35:void Init(constchar* file, constchar* func, int line);

36:

37:virtual std::string GetClassName() const;

38:

39:virtual std::string GetMessage() const;

40:

41:constchar* what() constthrow();

42:

43:const std::string& ToString() const;

44:

45: std::string GetStackTrace() const;

46:

47:protected:

48: std::string mMsg;

49:constchar* mFile;

50:constchar* mFunc;

51:int mLine;

52:

53:private:

54:enum { MAX_STACK_TRACE_SIZE = 50 };

55:void* mStackTrace[MAX_STACK_TRACE_SIZE];

56: size_t mStackTraceSize;

57:mutable std::string mWhat;

58: };

59:

60:class ExceptionDerived : public ExceptionBase

61: {

62:public:

63: MY_DEFINE_EXCEPTION(ExceptionDerived, ExceptionBase);

64: };

65:

66:#endif

这个头文件首先定义了两个宏,这里先暂时不管他,我先来解释一下ExceptionBase,它继承自std::exception,std::exception里面其实已经提供了一些功能了,但是比较弱,为了实现我们上文提到的功能,这里只是继承了std:exception的借口,也就是what()函数。

上面的接口应该比较好理解,45行的GetStackTrace是打印当前的调用栈,49-51行分别存储了当前出现exception的源文件名,函数名,行号,54行定义了最大的调用栈显示的深度,也就是显示50行。

60行显示了怎样定义一个新的异常类,这个就很方便了,通过MY_DEFINE_EXCEPTION宏去定义了一个继承类,详情见16行,这里不再细说,我这里想说说7行的MY_THROW宏,使用了3个内置的参数,__FILE__, __LINE__, __PRETTY_FUNCTION__, 他们分别是当前的文件名,行号,和函数名,他们的使用方法是在哪儿出现,其相应的值就是什么。

为什么这里要使用MY_THROW宏呢?其实是为了方便的把行号、文件名等加入进来,宏展开的时候是在一行上的,这样也使得行号与出错的行号保持一致,而且让代码更简单。

给出异常类的.cpp文件:

1:#include <execinfo.h>

2:#include <stdlib.h>

3:#include <cxxabi.h>

4:

5:#include

6:#include

7:

8:#include"exception_test.h"

9:

10:usingnamespace std;

11:

12: ExceptionBase::ExceptionBase(const std::string& msg) throw()

13: : mMsg(msg),

14: mFile(""),

15: mFunc(""),

16: mLine(-1),

17: mStackTraceSize(0)

18: {}

19:

20: ExceptionBase::~ExceptionBase() throw()

21: {}

22:

23:void ExceptionBase::Init(constchar* file, constchar* func, int line)

24: {

25: mFile = file;

26: mFunc = func;

27: mLine = line;

28: mStackTraceSize = backtrace(mStackTrace, MAX_STACK_TRACE_SIZE);

29: }

30:

31: std::string ExceptionBase::GetClassName() const

32: {

33:return"ExceptionBase";

34: }

35:

36:constchar* ExceptionBase::what() constthrow()

37: {

38:return ToString().c_str();

39: }

40:

41:const std::string& ExceptionBase::ToString() const

42: {

43:if (mWhat.empty())

44: {

45: stringstream sstr("");

46:if (mLine > 0)

47: {

48: sstr << mFile << "(" << mLine << ")";

49: }

50: sstr << ": " << GetClassName();

51:if (!GetMessage().empty())

52: {

53: sstr << ": " << GetMessage();

54: }

55: sstr << "\nStack Trace:\n";

56: sstr << GetStackTrace();

57: mWhat = sstr.str();

58: }

59:return mWhat;

60: }

61:

62: std::string ExceptionBase::GetMessage() const

63: {

64:return mMsg;

65: }

66:

67: std::string ExceptionBase::GetStackTrace() const

68: {

69:if (mStackTraceSize == 0)

70:return"\n";

71:char** strings = backtrace_symbols(mStackTrace, 10);

72:if (strings == NULL) // Since this is for debug only thus

73:// non-critical, don't throw an exception.

74:return"\n";

75:

76: std::string result;

77:for (size_t i = 0; i < mStackTraceSize; ++i)

78: {

79: std::string mangledName = strings[i];

80: std::string::size_type begin = mangledName.find('(');

81: std::string::size_type end = mangledName.find('+', begin);

82:if (begin == std::string::npos || end == std::string::npos)

83: {

84: result += mangledName;

85: result += '\n';

86:continue;

87: }

88: ++begin;

89:int status;

90:char* s = abi::__cxa_demangle(mangledName.substr(begin, end-begin).c_str(),

91: NULL, 0, &status);

92:if (status != 0)

93: {

94: result += mangledName;

95: result += '\n';

96:continue;

97: }

98: std::string demangledName(s);

99: free(s);

100:// Ignore ExceptionBase::Init so the top frame is the

101:// user's frame where this exception is thrown.

102://

103:// Can't just ignore frame#0 because the compiler might

104:// inline ExceptionBase::Init.

105: result += mangledName.substr(0, begin);

106: result += demangledName;

107: result += mangledName.substr(end);

108: result += '\n';

109: }

110: free(strings);

111:return result;

112: }

113:

114:/*

115: * test-main

116: */

117:int f2()

118: {

119: MY_THROW(ExceptionDerived, "f2 throw");

120: }

121:void f1()

122: {

123:try

124: {

125: f2();

126: }

127:catch (ExceptionDerived& e)

128: {

129: cout << e.what() << endl;

130: }

131: }

132:int main()

133: {

134: f1();

135: }

这是函数的实现代码,其他的都比较好理解,67行的GetStackTrace是相对复杂一点的,里面用backtrace函数去获取了当前调用栈的层数,用backtrace_symbols去获取当前调用栈的符号,而且__cxa_demangle函数的使用也值得去看看,这里不再细说了。

117行后展示了一个测试代码,代码虽然定义比较麻烦,不过使用还是很方便的:)。

 

转自:http://www.cr173.com/html/7723_1.html

分享到:
评论

相关推荐

    C与C++中的异常处理

    Microsoft在C++编译器中对异常处理进行了扩展,引入了结构化异常处理(Structured Exception Handling,SEH),这是一种专为Windows环境设计的异常处理机制。SEH能够处理由硬件错误、操作系统服务调用失败等引发的...

    C++程序设计语言 The C++ Programming Language

    《C++程序设计语言》是Bjarne Stroustrup所著的经典C++编程教材,第四版的出版进一步完善了对C++11及后续标准的解释。这本书详细介绍了C++语言的基础、高级特性以及现代编程实践,对于学习和理解C++编程语言具有极其...

    李龙澍 C++ 程序设计课件

    它的语法深受C语言的影响,但同时引入了类、模板、异常处理等高级特性,使得C++在功能和灵活性上更胜一筹。李龙澍的课件将详细讲解这些内容,包括变量、数据类型、运算符、控制流程语句(如if-else、for、while)、...

    C++语言程序设计 第4版 学生用书

    它的设计目标是提供高效的代码执行,强大的类型检查,以及对低级编程的直接控制,同时支持高级编程特性如类、模板和异常处理。在C++语言程序设计的学习过程中,学生将接触到以下几个核心知识点: 1. **基本语法**:...

    C++程序设计第三版

    由Bjarne Stroustrup于1979年在贝尔实验室开始设计开发,它在C语言的基础上增加了类、模板、命名空间等特性,增强了类型检查和异常处理,旨在提供更高效、更灵活的代码编写能力。 本书的知识点主要包括以下几个方面...

    C++箴言:视类设计为类型设计

    作为C++开发者,在开发过程中会花费大量时间来扩展和完善自己的类型系统。这意味着不仅仅是设计类本身,更是设计类型。在类设计的过程中,需要关注诸如重载函数和运算符、控制内存分配与回收、定义对象的初始化与...

    C++课程设计——酒店客房预订系统

    C++是一种面向对象的编程语言,它在C语言的基础上增加了类、模板、异常处理等高级特性。在酒店客房预订系统的开发中,C++的面向对象特性使得我们可以创建清晰的类结构来表示不同的实体,如客户、房间、订单等,使得...

    c++万年历课程设计

    1. 模块概述:设计中可能包括输入模块、日期处理模块、输出模块、异常处理模块等。 2. 头文件及全局变量:可能使用iostream、ctime等头文件,全局变量可能包含当前日期和时间的信息。 3. 模块1(输入模块):负责...

    用C++编写数字转换成汉字大写的程序

    此外,为了提高效率,可以考虑使用字符串流(stringstream)或模板类(template)来处理不同类型的数字,如int、double等。同时,为了保证程序的健壮性,还需要进行充分的测试,覆盖各种可能的输入情况,包括异常...

    C++语言程序设计(第2版) 吕凤翥编著

    8. **异常处理**:通过try-catch机制,C++提供了异常处理来捕获和处理运行时错误。 9. **内存管理**:包括动态内存分配(new和delete操作)和智能指针,以防止内存泄漏。 10. **输入/输出流**:C++的iostream库...

    《C++面向对象程序设计》第2版编程题答案

    4. 考虑异常处理:在必要时添加异常处理代码,增强程序的健壮性。 5. 编写测试用例:编写多种测试用例,验证代码的正确性和边界条件的处理。 这个压缩包中的"excercise"文件可以帮助学习者在实践中掌握这些概念,并...

    visual C++ 课程设计 课件下载

    6. **异常处理**:理解和应用异常处理机制是编程中重要的部分,它可以确保程序在遇到错误时能够优雅地处理,而不是突然崩溃。 7. **调试技巧**:学会使用Visual Studio的调试工具,如设置断点、查看变量值、单步...

    c++课程设计之车票管理系统 (加强版)

    在“C++课程设计之车票管理系统(加强版)”中,我们主要关注的是如何利用C++编程语言构建一个功能完善的车票预订系统。这个系统不仅包含基础的车票管理功能,还着重于异常处理和界面美化,以提供更友好的用户体验。...

    郑莉C++程序设计第四版PPT

    在本书中,作者郑莉将带领读者逐步探索C++的世界,从基础语法开始,如变量、数据类型、运算符、流程控制,到更高级的主题,如类与对象、继承、多态、模板、异常处理等。书中不仅涵盖了C++的基础知识,也包括了C++11...

    c++课程设计出租车管理系统

    2. **异常处理**:设置异常处理机制,确保程序在遇到错误时能够优雅地退出。 3. **多线程**:可能需要使用多线程技术处理并发的订单请求,提高系统响应速度。 4. **文件存储**:对于大量数据,可选择使用文件存储...

    学生类的选课系统C++

    - **异常处理**:对于可能出现的异常情况进行妥善处理,比如避免数组越界等问题。 - **用户体验**:提供清晰的菜单提示和操作指南,使用户能够快速了解如何使用系统。 综上所述,这个学生选课系统通过定义学生和...

    C++程序设计原理与实践

    根据提供的标题“C++程序设计原理与实践”以及描述中的相同表述,我们可以推断出这份文档主要关注的是C++编程语言的设计原则、理论基础及其在实际应用中的具体操作方法。虽然给定的部分内容并未提供实质性信息,但从...

    C++ 程序设计教程(第二版) 钱能 PPT课件.rar

    《C++程序设计教程》是钱能教授撰写的一本经典的编程教材,第二版更是对第一版进行了深入的修订和完善,旨在帮助初学者和有经验的程序员更好地理解和掌握C++这门强大的编程语言。钱能教授在书中以清晰易懂的方式阐述...

    c++课程设计之计算器

    6. **异常处理**:在执行除法或其他可能抛出错误的操作时,应使用异常处理(`try-catch`块)来确保程序的健壮性。例如,当除数为零时,可以抛出一个异常并给出相应提示。 7. **函数封装**:将每种运算封装为独立的...

Global site tag (gtag.js) - Google Analytics