我们的异常处理类的features

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

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

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 <exception>
   5: #include <string>
   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(const char* file, const char* func, int line);
  36:  
  37:     virtual std::string GetClassName() const;
  38:  
  39:     virtual std::string GetMessage() const;
  40:  
  41:     const char* what() const throw();
  42:  
  43:     const std::string& ToString() const;
  44:  
  45:     std::string GetStackTrace() const;
  46:  
  47: protected:
  48:     std::string mMsg;
  49:     const char* mFile;
  50:     const char* 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 <iostream>
   6: #include <sstream>
   7:  
   8: #include "exception_test.h"
   9:  
  10: using namespace std;
  11:  
  12: ExceptionBase::ExceptionBase(const std::string& msg) throw()
  13:     : mMsg(msg),
  14:       mFile("<unknown file>"),
  15:       mFunc("<unknown func>"),
  16:       mLine(-1),
  17:       mStackTraceSize(0)
  18: {}
  19:  
  20: ExceptionBase::~ExceptionBase() throw()
  21: {}
  22:  
  23: void ExceptionBase::Init(const char* file, const char* 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: const char* ExceptionBase::what() const throw()
  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 "<No stack trace>\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 "<Unknown error: backtrace_symbols returned NULL>\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行后展示了一个测试代码,代码虽然定义比较麻烦,不过使用还是很方便的:)。

from: http://www.cnblogs.com/LeftNotEasy/archive/2010/10/30/1865364.html

如何写一个完善的c++异常处理类相关推荐

  1. 笔试题:写一个ArrayList 的动态代理类

    package com.zhangxueliang.demo;import java.lang.reflect.InvocationHandler; import java.lang.reflect. ...

  2. php 日期单数 复数,参考ROR中的单复数转换,写一个PHP的单复数转换类

    1.[代码]参考ROR中的单复数转换,写一个PHP的单复数转换类$inflector = new Inflector(); echo $inflector->pluralize('bus') . ...

  3. 写一个工具生成数据库实体类

    写一个java工具从数据库生成实体类 开始写之前的说明 ​ 这只是一个简单的解析数据库建表语句之后,根据解析结果生成java文件的一个简单工具.写的原因有两个. ​ 1:项目中没有人写实体类的注释,字 ...

  4. java json帮助类_java 写一个JSON解析的工具类

    上面是一个标准的json的响应内容截图,第一个红圈"per_page"是一个json对象,我们可以根据"per_page"来找到对应值是3,而第二个红圈&quo ...

  5. 缓存类java_用Java写一个简单的缓存操作类

    前言 使用缓存已经是开发中老生常谈的一件事了,常用专门处理缓存的工具比如Redis.MemCache等,但是有些时候可能需要一些简单的缓存处理,没必要用上这种专门的缓存工具,那么自己写一个缓存类最合适 ...

  6. 用百行Python代码写一个关于德州扑克的类

    德州扑克是一款竞技类的扑克游戏,随着人类在各种竞技游戏被ai所碾压,我思考有着像棋类这种考验计算能力封闭式的游戏被电脑统治在所难免,德州扑克这种考验人性和心理的游戏在2017年也被ai攻克,本人是德州 ...

  7. Java写一个接口和两个类

    编写一个接口和两个类,要求如下: (1)设计一个学生功能的接口,实现平均成绩的计算和基本信息的输出. (2)设计一个学生类实现了学生接口,学生类的基本信息有:学号.姓名.年龄.各科成绩(假设一共5门课 ...

  8. 用python写一个解一元二次方程的类

    第一步 明确需要的变量 要解一元二次方程,肯定先把它化成一般式:ax^2+bx+c=0,我们需要用到的参数是a,b,c还有一个判别式"▲" ,可以随意给个名字,我这里给的g. 第二 ...

  9. layer code 200转码 写一个后台封装通用实体类

    package auto.system.common.entity; import java.io.Serializable; import java.util.List; @SuppressWarn ...

最新文章

  1. pandas 修改数据和数据类型
  2. php图标按钮,CSS如何创建图像图标按钮(附代码)
  3. matlab中rat=1函数,matlab中的format rat是什么意思
  4. 我的第一篇学术论文发表出来啦啦啦啊!!!
  5. cello 有关trigger
  6. 天梯赛 喊山 bfs
  7. gerrit Cannot Merge
  8. apache开源项目--Lens
  9. 什么是IDE(集成开发环境)?
  10. caffe 使用cudnn 加速报错
  11. Dev XtraTreeList 学习笔记
  12. PyTorch 1.0稳定版正式发布,并向开发者提供免费AI课程
  13. Python面向对象中的多态与静态语言(C++,Java)的区别
  14. API激光跟踪仪SDK基础使用
  15. 宏晶微MS2130 USB3.0高清视频采集芯片应用于直播导播一体机
  16. MapReduce强化实验
  17. wxWidgets GUI 编程介绍
  18. 移动端 背景音乐 自动播放
  19. 服务器不能读取无线网卡,无线网卡无法获得IP地址的解决方案
  20. FactoryBean的使用~

热门文章

  1. Facebook Libra不会和主权货币竞争
  2. 30年来我只坚持三件事
  3. 关于催收那些事儿(下)
  4. 布道微服务_10注册中心与RPC框架的选型
  5. MySQL - order by和 group by 优化初探
  6. Spring MVC-09循序渐进之文件上传(基于Apache Commons FileUpload)
  7. Toolbar-5.0新特性
  8. 信息提醒之对话框(AlertDialog + ProgressDialog)-更新中
  9. linux里hba状态_在Linux/Unix平台查看HBA卡的WWN号 和状态
  10. python 合并两个排序的链表(递归解法)