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. 玩转Vim-札记(一)

    玩转Vim-札记(一) 简介 在这个蔚蓝色的星球上,流传着两大神器的传说:据说Emacs是神的编辑器,而Vim是编辑器之神.一些人勇敢地拾起了Vim或Emacs,却发现学习曲线陡峭而漫长,还是有一些人 ...

  2. 制作一个简易计算器——基于Android Studio实现

    一个计算器Android程序的源码部分分为主干和细节两部分. 一.主干 1. 主干的构成 计算器的布局 事件(即计算器上的按钮.文本框)监听 实现计算 2. 详细解释 假设我们的项目名为Calcula ...

  3. 利用java中的BigInteger实现进制转换

    [原创] java中的进制BigInteger十分的强大,而且好用,他可以表示任意大的整数,同时还可以进行进制转换,十分的方便, 代码示例: package com.jiajia.demo_1; im ...

  4. PTA循环,函数,数组作业

    PTA循环实验作业 题目一:统计素数并求和 ### 1.PTA提交列表 2.设计思路(+流程图) 先定义变量(包含素数区间,循环次数,除数,素数个数记录和和的记录) 输入范围 一重循环:循环提取自然数 ...

  5. lintcode-124-最长连续序列

    124-最长连续序列 给定一个未排序的整数数组,找出最长连续序列的长度. 说明 要求你的算法复杂度为O(n) 样例 给出数组[100, 4, 200, 1, 3, 2],这个最长的连续序列是 [1, ...

  6. 【PHP】- 魔术常量

    PHP 向它运行的任何脚本提供了大量的预定义常量.不过很多常量都是由不同的扩展库定义的,只有在加载了这些扩展库时才会出现,或者动态加载后,或者在编译时已经包括进去了.有八个魔术常量它们的值随着它们在代 ...

  7. [bzoj1018] [SHOI2008]堵塞的交通

    题目描述 有一天,由于某种穿越现象作用,你来到了传说中的小人国.小人国的布局非常奇特,整个国家的交通系统可以被看成是一个22行CC列的矩形网格,网格上的每个点代表一个城市,相邻的城市之间有一条道路,所 ...

  8. [洛谷P4782]【模板】2-SAT 问题

    题目大意:有$n$个布尔变量 $x_1 \sim x_n$,另有$m$个需要满足的条件,每个条件的形式都是"$x_i$ 为$true/false$或$x_j$为$true/false$&qu ...

  9. EOS docker开发环境

    EOS Wiki提供了有关如何使用docker容器编译最新版本代码的说明.但可能有它自己的一些问题,因此我们鼓励你在学习时引用下面镜像.这样最初会更容易,更快. 如果你还没有安装docker,请在此处 ...

  10. NOIP2010 codevs1069 洛谷P1525 关押罪犯

    Problem Description S 城现有两座监狱,一共关押着N 名罪犯,编号分别为1~N.他们之间的关系自然也极 不和谐.很多罪犯之间甚至积怨已久,如果客观条件具备则随时可能爆发冲突.我们用 ...