Linux 下 C++ 异常处理技巧

处理固有语言局限性的四种技术

处理 C++ 中的异常会在语言级别上遇到少许隐含限制,但在某些情况下,您可以绕过它们。学习各种利用异常的方法,您就可以生产更可靠的应用程序。

1 评论

Sachin O. Agrawal(sachin_agrawal@in.ibm.com), 高级软件工程师, IBM Software Labs, India

2005 年 3 月 07 日

  • 内容

在 IBM Bluemix 云平台上开发并部署您的下一个应用。

开始您的试用

保留异常来源信息

在 C++中,无论何时在处理程序内捕获一个异常,关于该异常来源的信息都是不为人知的。异常的具体来源可以提供许多更好地处理该异常的重要信息,或者提供一些可以附加到错误日志的信息,以便以后进行分析。

为了解决这一问题,可以在抛出异常语句期间,在异常对象的构造函数中生成一个堆栈跟踪。ExceptionTracer 是示范这种行为的一个类。

清单 1. 在异常对象构造函数中生成一个堆栈跟踪
 // Sample Program:
// Compiler: gcc 3.2.3 20030502
// Linux: Red Hat
#include <execinfo.h>
#include <signal.h>
#include <exception>
#include <iostream>
using namespace std;
/////////////////////////////////////////////
class ExceptionTracer
{
public:
ExceptionTracer()
{
void * array[25];
int nSize = backtrace(array, 25);
char ** symbols = backtrace_symbols(array, nSize);
for (int i = 0; i < nSize; i++)
{
cout << symbols[i] << endl;
}
free(symbols);
}
};
 

回页首

管理信号

每当进程执行一个令人讨厌的动作,以致于 Linux™ 内核发出一个信号时,该信号都必须被处理。信号处理程序通常会释放一些重要资源并终止应用程序。在这种情况下,堆栈上的所有对象实例都处于未破坏状态。另一方面,如果这些信号被转换成 C++ 异常,那么您可以优雅地调用其构造函数,并安排多层 catch 块,以便更好地处理这些信号。

清单 2 中定义的 SignalExceptionClass,提供了表示内核可能发出信号的 C++ 异常的抽象。SignalTranslator 是一个基于SignalExceptionClass 的模板类,它通常用来实现到 C++ 异常的转换。在任何瞬间,只能有一个信号处理程序处理一个活动进程的一个信号。因此,SignalTranslator 采用了 singleton 设计模式。整体概念通过用于 SIGSEGV 的 SegmentationFault 类和用于 SIGFPE 的FloatingPointException 类得到了展示。

清单 2. 将信号转换成异常
 template <class SignalExceptionClass> class SignalTranslator
{
private:
class SingleTonTranslator
{
public:
SingleTonTranslator()
{
signal(SignalExceptionClass::GetSignalNumber(), SignalHandler);
}
static void SignalHandler(int)
{
throw SignalExceptionClass();
}
};
public:
SignalTranslator()
{
static SingleTonTranslator s_objTranslator;
}
};
// An example for SIGSEGV
class SegmentationFault : public ExceptionTracer, public exception
{
public:
static int GetSignalNumber() {return SIGSEGV;}
};
SignalTranslator<SegmentationFault> g_objSegmentationFaultTranslator;
// An example for SIGFPE
class FloatingPointException : public ExceptionTracer, public exception
{
public:
static int GetSignalNumber() {return SIGFPE;}
};
SignalTranslator<FloatingPointException> g_objFloatingPointExceptionTranslator;
 

回页首

管理构造函数和析构函数中的异常

在全局(静态全局)变量的构造和析构期间,每个 ANSI C++ 都捕获到异常是不可能的。因此,ANSI C++ 不建议在那些其实例可能被定义为全局实例(静态全局实例)的类的构造函数和析构函数中抛出异常。换一种说法就是永远都不要为那些其构造函数和析构函数可能抛出异常的类定义全局(静态全局)实例。不过,如果假定有一个特定编译器和一个特定系统,那么可能可以这样做,幸运的是,对于 Linux 上的 GCC,恰好是这种情况。

使用 ExceptionHandler 类可以展示这一点,该类也采用了 singleton 设计模式。其构造函数注册了一个未捕获的处理程序。因为每次只能有一个未捕获的处理程序处理一个活动进程,构造函数应该只被调用一次,因此要采用 singleton 模式。应该在定义有问题的实际全局(静态全局)变量之前定义 ExceptionHandler 的全局(静态全局)实例。

清单 3. 处理构造函数中的异常
 class ExceptionHandler
{
private:
class SingleTonHandler
{
public:
SingleTonHandler()
{
set_terminate(Handler);
}
static void Handler()
{
// Exception from construction/destruction of global variables
try
{
// re-throw
throw;
}
catch (SegmentationFault &)
{
cout << "SegmentationFault" << endl;
}
catch (FloatingPointException &)
{
cout << "FloatingPointException" << endl;
}
catch (...)
{
cout << "Unknown Exception" << endl;
}
//if this is a thread performing some core activity
abort();
// else if this is a thread used to service requests
// pthread_exit();
}
};
public:
ExceptionHandler()
{
static SingleTonHandler s_objHandler;
}
};
//////////////////////////////////////////////////////////////////////////
class A
{
public:
A()
{
//int i = 0, j = 1/i;
*(int *)0 = 0;
}
};
// Before defining any global variable, we define a dummy instance
// of ExceptionHandler object to make sure that
// ExceptionHandler::SingleTonHandler::SingleTonHandler() is invoked
ExceptionHandler g_objExceptionHandler;
A g_a;
//////////////////////////////////////////////////////////////////////////
int main(int argc, char* argv[])
{
return 0;
}
 

回页首

处理多线程程序中的异常

有时一些异常没有被捕获,这将造成进程异常中止。不过很多时候,进程包含多个线程,其中少数线程执行核心应用程序逻辑,同时,其余线程为外部请求提供服务。如果服务线程因编程错误而没有处理某个异常,则会造成整个应用程序崩溃。这一点可能是不受人们欢迎的,因为它会通过向应用程序传送不合法的请求而助长拒绝服务攻击。为了避免这一点,未捕获处理程序可以决定是请求异常中止调用,还是请求线程退出调用。清单 3 中 ExceptionHandler::SingleTonHandler::Handler() 函数的末尾处展示了该处理程序。

 

回页首

结束语

我简单地讨论了少许 C++ 编程设计模式,以便更好地执行以下任务:

  • 在抛出异常的时候追踪异常的来源。
  • 将信号从内核程序转换成 C++ 异常。
  • 捕获构造和/或析构全局变量期间抛出的异常。
  • 多线程进程中的异常处理。

我希望您能采用这些技巧中的一些来开发无忧代码。

C++异常~二 转的更多相关文章

  1. java提高篇(十七)-----异常(二)

          承接上篇博文:java提高篇-----异常(一) 五.自定义异常 Java确实给我们提供了非常多的异常,但是异常体系是不可能预见所有的希望加以报告的错误,所以Java允许我们自定义异常来表 ...

  2. java 异常二

    一 捕获异常try…catch…finally 捕获:Java中对异常有针对性的语句进行捕获,可以对出现的异常进行指定方式的处理 捕获异常格式: try { //需要被检测的语句. } catch(异 ...

  3. Java异常(二) 《Effective Java》中关于异常处理的几条建议

    概要 本章是从<Effective Java>摘录整理出来的关于异常处理的几条建议.内容包括:第1条: 只针对不正常的情况才使用异常第2条: 对于可恢复的条件使用被检查的异常,对于程序错误 ...

  4. 【C++】异常简述(二):C++的异常处理机制

    上文简述了在C语言中异常的处理机制,本文主要讲解C++中的异常处理. 一.异常的语法格式 在C++中,异常的抛出和处理主要使用了以下三个关键字:try. throw . catch.其格式如下: 当我 ...

  5. Hibernate 非常见异常集合

    异常一:org.hibernate.AnnotationException: Collection has neither generic type or OneToMany.targetEntity ...

  6. hadoop安装遇到的各种异常及解决办法

    hadoop安装遇到的各种异常及解决办法 异常一: 2014-03-13 11:10:23,665 INFO org.apache.hadoop.ipc.Client: Retrying connec ...

  7. [转]菜鸟程序员之Asp.net MVC Session过期异常的处理

    本文转自:http://www.cnblogs.com/JustRun1983/p/3377652.html 小赵是刚毕业的计算机专业方面的大学生,4年的大学时间里面,他读过了很多编程方面的数据,也动 ...

  8. 构建spring+mybatis+redis架构时遇到的小异常

    异常一: Caused by: java.lang.NoSuchMethodError: org.springframework.beans.BeanUtils.instantiateClass(Lj ...

  9. 解决jetty7.0.pre5启动时报ClassNotFoundException: javax.interceptor.InvocationContext异常的问题

    一.背景介绍:最近项目在使用maven命令行运行jetty服务器时出现ClassNotFoundException: javax.interceptor.InvocationContext异常 二.环 ...

随机推荐

  1. Vue学习(五):列表渲染

    <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8&quo ...

  2. 关于redis一些问题记录

    问题一:启动redis时出现警告,使用下列命令(已解决) 问题二:启动时,需要解决的警告(未解决) 问题三:使用自己的配置文件启动redis时,可能会遇到: Could not connect to ...

  3. 开发react的一些记录

    1.keyboard事件返回的对象SyntheticKeyboardEvent全部是null 解决方法:SyntheticKeyboardEvent的type,which,timeStamp可以得到你 ...

  4. hyperledger composer

    hyperledger composer 网站搜集 https://hyperledger.github.io/composer/latest/introduction/introduction.ht ...

  5. 树莓派配置 USB 无线网卡

    树莓派配置 USB 无线网卡来上网的过程. 本人使用的USB无线网卡型号:EP-N8508GS(树莓派专用型号) 一.检查 USB 无线网卡是否已经正确识别 将无线 USB 网卡插入树莓派后启动树莓派 ...

  6. IDEA使用maven构建时控制台中文乱码的解决办法

    使用maven clean install 项目时控制台中文乱码,解决办法如下: Setting->maven->runner VMoptions: -Dfile.encoding=UTF ...

  7. 最短路径——Bellman-Ford算法以及SPFA算法

    说完dijkstra算法,有提到过朴素dij算法无法处理负权边的情况,这里就需要用到Bellman-Ford算法,抛弃贪心的想法,牺牲时间的基础上,换取负权有向图的处理正确. 单源最短路径 Bellm ...

  8. 【SSH】——spring的控制反转和依赖注入

    spring是一个轻量级的容器框架,主要是为了使企业的开发变得简单.高效.无论是从大小还是开销来讲,他都可以算是轻量级的,也是非侵入性的. 下图是spring的框架示意图,说到spring,就不得不提 ...

  9. [Elasticsearch] 多字段搜索 (二) - 最佳字段查询及其调优(转)

    最佳字段(Best Fields) 假设我们有一个让用户搜索博客文章的网站,就像这两份文档一样: PUT /my_index/my_type/1 { "title": " ...

  10. maven中进行go的编译

    maven提供的插件maven-antrun-plugin真是个好东东,使得maven可以利用ant的很多功能. 最近需要实现在maven中实现对go代码的编译,添加如下代码在pom文件中即可. &l ...