try-catch应用心得

来源:互联网 发布:python 对象转字符串 编辑:程序博客网 时间:2024/06/07 07:25
一、C/C++编程中的异常机制
1,函数返回值判断
普通的函数无返回值类型,如void func(intn)。如果函数内部有逻辑错误,仅仅是return,函数的调用者无法知道该函数是正常执行完毕返回的还是异常返回的。如果将返回值改为bool型或者int型,则调用者可以通过判断返回值,来确定被调函数是否有异常。
2,全局变量
程序中定义一个全局变量,任何函数发生异常时,都可以去修改该全局变量,调用者通过查看该变量来判断是否发生了异常。C语言数学库使用的就是这种方法,它使用的全局变量名为errno。
3,try-catch
使用try块包裹可能会出现异常的代码,特别是多层函数调用的地方,在出现逻辑异常的地方,用throw抛出异常,然后使用catch捕获类型匹配的异常,进行集中处理。

二、为什么要用try-catch
1,错误集中处理,程序的结构更清晰,代码服用率更高。catch语句块会集中处理各类异常,可以统一设计异常处理机制和对异常统一分类,也可以减少其他地方的异常检查和返回值检查。
2,解放函数返回值。有些函数设计时,其返回值是有意义的,如获取器。使用try-catch就可以保持函数的原样,降低开发阶段对函数设计时,选择某中形式的出入参数和返回值的考虑。选择自由度更高。
3,try-catch多于多层的函数嵌套调用,尤其有效。可以跨层进行异常捕获,不会由于开发过程中某一层没有设计异常检查机制而遗漏某个异常。
4,try-catch可以轻易捕获意想不到的逻辑异常,另外,也可以捕获非逻辑异常(指针越界等),如运行时异常(浮点运算的溢出,开空间失败,读文件失败等)。
5,try-catch也可用于控制程序执行流程,下次再探讨try-catch与状态机结合,以及try-catch在STL算法functor中对程序流程的完善。

三、如何使用try-catch
1,简单的程序,可以直接抛出const char*或enum,再进行对应的类型捕获即可。
2,复杂程序,一般需要设计“自定义异常类“,如:class CustomException: publicstd::logic_error。
一般地,需要程序员特别注意的是开发过程中的逻辑错误或逻辑遗漏,是用try-catch可以很好的及时发现这类问题,然后及时修复逻辑错误。但是,随着开发过程中的代码变更,还会有新的逻辑错误出现,try-catch的作用就相对于一个过滤器。所以,我在设计“自定义异常类“时,会首先关注"std::logic_error"类型的异常。
3,catch语句群进行分类分层捕获异常。它的层次与类的继承层次相反,这样可以最大限度捕获不同的异常。如下:

catch(CustomException & e)
    {
qCritical() << e.what();
        emit SIGNAL_ErrorMessage(e.GetErrorLevel(), e.what());
    }
    catch(std::logic_error & e)
    {
        qCritical() << e.what();
        emit SIGNAL_ErrorMessage(Fatal, "Non-custom logic error");
    }
    catch(std::exception & e)
    {
qCritical() << e.what();
        emit SIGNAL_ErrorMessage(Fatal, "Non-logic error");
    }
第一层捕获自定类型匹配的异常;第二层捕获自定义类型之外的逻辑错误;第三层捕获非逻辑错误,如开控件失败等等。从这个分层即可看出,try-catch处理能像其他异常机制一样捕获意料之中的异常,还能捕获意想不到的异常。
另附,我设计的一个简单的"自定义异常类”:
#ifndef CUSTOMEXCEPTION_H
#define CUSTOMEXCEPTION_H

#include 
#include 
// 错误等级
#ifndef ERROR_LEVEL
#define ERROR_LEVEL
typedef enum
{
    Common  = 1,
    Warning,
    Fatal
} ErrorLevel;
#endif    // ERROR_LEVEL

class CustomException : public std::logic_error
{
public:
    explicit CustomException(int iErrorLevel, const std::string & s = "Default logic error of custom type\n");
    int GetErrorLevel() const { return m_iErrorLevel; }
    ~CustomException();
private:
    int m_iErrorLevel;
};

#endif // CUSTOMEXCEPTION_H

四、构造函数和析构函数可以抛出异常吗?
从语法上来说,构造函数和析构函数都可以抛出异常。但从逻辑上和风险控制上,构造函数可以,析构函数不推荐抛出异常。参考:构造函数和析构函数。
一般地,构造函数中会有函数调用、new对象、读配置文件等操作,最好用try-catch包裹起来,可以捕获意料之外的异常,减少程序bug定位的时间。析构函数则不推荐抛出异常,参考《effictiveC++》item8: Prevent exception from leaving destructors。
1,析构函数绝对不要吐出异常。如果一个被析构函数调用的函数可能抛出异常,析构函数应该捕捉任何异常,然后吞下它们(不传播)或结束程序。
2,如果客户需要对某个操作函数运行期间抛出的异常做出反应,那么class应该提供一个普通函数(而非在析构函数中)执行该操作。

0 0
原创粉丝点击