Linux下 C++措施的异常处理惩罚能力
副标题#e#
处理惩罚 C++ 中的异常会在语言级别上碰着少许隐含限制,但在某些环境下,您可以绕过它们。进修各类操作异常的要领,您就可以出产更靠得住的应用措施。
保存异常来历信息
在 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);
}
};
#p#副标题#e#
打点信号
每当历程执行一个令人讨厌的行动,乃至于 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;
}
处理惩罚多线程措施中的异常
有时一些异常没有被捕捉,这将造成历程异常中止。
#p#分页标题#e#
不外许多时候,历程包括多个线程,个中少数线程执行焦点应用措施逻辑,同时,其余线程为外部请求提供处事。假如处事线程因编程错误而没有处理惩罚某个异常,则会造成整个应用措施瓦解。这一点大概是不受人们接待的,因为它会通过向应用措施传送不正当的请求而助长拒绝处事进攻。为了制止这一点,未捕捉处理惩罚措施可以抉择是请求异常中止挪用,照旧请求线程退出挪用。清单 3 中 ExceptionHandler::SingleTonHandler::Handler() 函数的末端处展示了该处理惩罚措施。
竣事语
本文简朴地接头了少许 C++ 编程设计模式,以便更好地执行以下任务:
· 在抛出异常的时候追踪异常的来历。
· 将信号从内核措施转换成 C++ 异常。
·捕捉结构和/或析构全局变量期间抛出的异常。
·多线程历程中的异常处理惩罚。