现在的位置: 首页 > 综合 > 正文

一 如何合适处理程序的异常

2013年01月27日 ⁄ 综合 ⁄ 共 2691字 ⁄ 字号 评论关闭

关于函数可能抛出的异常的一些声明
void f() throw() 表示f不允许抛出任何异常,即f是异常安全的。
void f() throw(...) 表示f可以抛出任何形式的异常。
void f() throw(exceptionType); 表示f只能抛出exceptionType类型的异常。

声明一个不抛出异常的函数后,你有责任保证在你的函数的实现里面不会抛出异常。

附上别人的一些心得,有空可以验证一下:
1) 函数后面声明 throw() 只是接口的提供者和接口的使用者间的默契或称协议。
2) 这种协议不影响正常的异常处理流程。
3) vc2005 会在编译器对这种协议的遵守情况进行检查,并在为遵守协议的情况下 给出警告。

用于打印调用堆栈的辅助函数,可以在异常发生的时候打印事故现场 :(需要编译时加上-g -rdynamic两个选项)
std::string getStacktrace()
{
    int size = 16;
    void * array[16];
    int stack_num = backtrace(array, size);
    char ** stacktrace = backtrace_symbols(array, stack_num);
    for (int i = 0; i < stack_num; ++i)
    {
        printf("%s\n", stacktrace[i]);
    }
    free(stacktrace);
}

创建自己的异常类,用于存储额外的异常信息:错误码,堆栈,错误信息等
class BaseException:public std::runtime_error
{
public:
    BaseException(int  errorCode, const std::string& errorMessage = "",const std::string &btInfo = "");
    ~BaseException() throw();
 
    int  getErrorCode(void) const;//异常错误码
 
    const char* what() const throw();//异常描述

    const char *getBtInfo() const throw();//异常发生时的堆栈

  private:
int errorCode;
std::string m_errorMsg;
std::string m_btInfo;
};

BaseException::BaseException(int errorCode, const std::string& errorMessage,,const std::string &btInfo) :
        std::runtime_error(errorMessage)
{
    m_errorCode = errorCode;
    m_errorMsg = errorMessage;
    m_btInfo = btInfo;
}

BaseException::~BaseException() throw()
{
    
}

int BaseException::getErrorCode(void) const
{
    return m_errorCode;
}

const char* BaseException::what() const throw()
{
  return m_errorMsg.c_str();
}

const char* BaseException::getBtInfo() const throw()
{
  return m_errorMsg.c_str();
}

1在程序的最外围,应该用try/catch将程序入口包起来,防止因为有异常没捕捉导致程序core

int main()
{
try
{
...//你的代码
}
catch(BaseException &base_exception)
{
//可以将异常信息写入到日志,以供后面追踪
Log.write(base_exception.getErrorCode(),base_exception.what(),base_exception.getBtInfo());
}
catch(BaseException &std_exception)
{
//可以将异常信息写入到日志,以供后面追踪
Log.write(std_exception.what());
}
catch(...)
{
//可以将异常信息写入到日志,以供后面追踪
Log.write("unknown error");
}

}

 2 正确处理new的问题,不应该直接使用系统的new,因为系统级的new有可能会抛出bad_alloc &memExp这种类型的异常,比较合适的做法应该是用创建工厂,例如:

int * createInt()
{
    int * tmp = null;
   try
    {
tmp = new int;
    }

   catch(bad_alloc &memExp)
   {
    //这里可以将异常信息捕获,然后抛出去,也可以将错误信息和堆栈信息一起抛出去,这样,在外围捕捉到异常时,可以比较容易确定异常发生的位置
         BaseException bad_exception(0,memExp.what(),getStacktrace());
throw bad_exception;
   }
}

3 通过new创建的对象,应该用std::shared_ptr之类的只能包装起来,比较安全

4 底层的数据接口,一定要搞明白会不会抛异常,如果会,就需要跟new一样包装一下,不然一旦出错,没有错误信息和堆栈信息,你都不知道发生什么事,比如C++11中的std::stoi,std::stod
就是会抛异常,需要包装一下

5 多线程程序,子线程的异常是不会被主线程的try/catch捕获的,因此每个子线程需要自己捕捉自己线程内部可能发生的异常

6  现在来考虑这样一个构造函数:

Type() : m_a(new TypeA), m_b(new TypeB){}

  假设成员变量m_a和m_b是原始的指针类型,并且和Type内的申明顺序一致。这样的代码是不安全的,它存在资源泄漏问题,构造函数的失败回滚机制无法应对这样的问题。如果new TypeB抛出异常,new TypeA返回的资源是得不到释放机会的. 
解决方法:
shared_ptr<TypeA> m_a; shared_ptr<TypeB> m_b;这样,我们就可以轻而易举地写出异常安全的代码:

Type() : m_a(new TypeA), m_b(new TypeB){}

}

7 处理进程可能会收到的信号:

进程可能会收到系统发送的信号,如果不处理某些信号,进程的默认响应是终止,因此,为保证正确性,应该正确的处理信号。

抱歉!评论已关闭.