2012年3月20日星期二

[C++] 异常处理的便利性

[C++] 异常处理的便利性
   -- 侃侃而谈,try-throw-catch的好处有哪些?



## 背景

既然C++语法久以来就一直存在着针对异常处理支持,即try-throw-catch结构,那么该异常处理就有一定的实用之处,虽说在C++业界有众多的程序员们对此不断地批判着,其中也不乏大牛们。所以,我在此谈及其好处,一定有很多人表示抗议。抗议的暂且淡定,继续看看下面内容,同时欢迎指正。

## 观点
先说说我个人针对C++异常try-throw-catch结构的认识过程吧(至今),如下:

首先,刚开始从C过来学习C++时,语法上提供这样异常机制对于新手的我,简直耳目一新。
     -- 慕之敬之
然后,随着多年慢慢学习C++过程中,不断看到诸多对其批判与鄙视,而且其中不乏大牛们。
     -- 斥之远之
最后(至今),鉴于最近工作内容涉及的重构代码--剔除很多的异常逻辑,我“深深”发现try-throw-catch结构的异常机制针对复杂的错误情况处理,颇显便捷与实用的益处。
     -- 思之慎之

(PS. 以下内容表示看好C++异常处理,违背了多数程序员的观点,望轻拍。
其实,个人还保持比较反对C++异常处理的态度,以后照样慎用!)

## 示例
以简单代码的例子说说,其便捷与实用之处。如下:


const unsigned BUFFER_MAX_LEN = 64;

void read_file_e(FILE* f, char* buf, size_t buf_size)
{
    if (NULL == f)
        throw std::logic_error("f is NULL.");
    // ... read file ....
}

bool read_file_r(FILE* f, char* buf, size_t buf_size)
{
    if (NULL == f)
        return false;
    // ... read file ....
    return true;
}

class read_info_class
{
public:
    enum { ITEM_COUNT = 30, INFO_MAX_LEN = BUFFER_MAX_LEN };
public:
    read_info_class()
        : _file(0) {}

    read_info_class(FILE* file)
        : _file(file) {}
    ~read_info_class()
    {
        if (_file)
            fclose(_file);
    }

    void set_file (FILE* new_f)
    {
        if (_file)
            fclose(_file);
        _file = new_f;
    }
    void read_info_for_item1_e()
    {
        //assert (_file);
        string info;
        info.resize(INFO_MAX_LEN);
        read_file_e(_file, const_cast<char*>(info.c_str()), info.size());
        _info_items.push_back(info);
        read_file_e(_file, const_cast<char*>(info.c_str()), info.size());
        _info_items.push_back(info);
        // ... if read more items than 30...
    }

    void read_info_for_item2_e()
    {
        /* ... */
    }
    void read_info_for_item3_e()
    {
        /* ... */
    }
    void read_info_for_item4_e()
    {
        /* ... */
    }
    void read_info_for_item5_e()
    {
        /* ... */
    }
    // ... more ...

    bool read_info_for_item1_r()
    {
        //assert (_file);
        string info;
        info.resize(INFO_MAX_LEN);
        if (!read_file_r(_file, const_cast<char*>(info.c_str()), info.size()))
            return false;
        _info_items.push_back(info);
        if (!read_file_r(_file, const_cast<char*>(info.c_str()), info.size()))
            return false;
        _info_items.push_back(info);
        // ... if read more items than 30...

        return true;
    }

    bool read_info_for_item2_r()
    {
        /* ... */ return true;
    }
    bool read_info_for_item3_r()
    {
        /* ... */ return true;
    }
    bool read_info_for_item4_r()
    {
        /* ... */ return true;
    }
    bool read_info_for_item5_r()
    {
        /* ... */ return true;
    }
    // ... more ...

private:
    FILE* _file;
    vector<string> _info_items;
    // more other var...
};

void do_read_func_e()
{
    // do something ...
    FILE* file = NULL;
    // open file and ...
    vector<char> buf(BUFFER_MAX_LEN);
    read_file_e(file, &buf[0], buf.size());
    // handle buf's data ...
    read_file_e(file, &buf[0], buf.size());
    // more ...
    read_info_class read_info;
    // initialize read_info and ...
    read_info.read_info_for_item1_e();
    read_info.read_info_for_item2_e();
    read_info.read_info_for_item3_e();
    read_info.read_info_for_item4_e();
    read_info.read_info_for_item5_e();
    // more read_info_for_item5_e ...
    return;
}

bool do_read_func_r()
{
    // do something ...
    FILE* file = NULL;
    // open file and ...
    vector<char> buf(BUFFER_MAX_LEN);
    if (!read_file_r(file, &buf[0], buf.size()))
        return false;
    // handle buf's data ...
    if (!read_file_r(file, &buf[0], buf.size()))
        return false;
    // more ...
    read_info_class read_info;
    // initialize read_info and ...
    if (!read_info.read_info_for_item1_r() ||
            !read_info.read_info_for_item2_r() ||
            !read_info.read_info_for_item3_r() ||
            !read_info.read_info_for_item4_r() ||
            !read_info.read_info_for_item5_r() ) /// || more read_info_for_item*_r ...
        return false;
    // ...
    return true;
}
void test_read_func_e()
{
    // ...
    try
    {
        do_read_func_e();
    }
    catch (std::exception& e)
    {      // 里边一个throw操作,就直接到了错误处理地方了,很快吧^_^
        std::cerr << " catch error:" << e.what() << endl;
    }
    // ... more ...
}

void test_read_func_r()
{
    if (!do_read_func_r())
    {     // 经过了一系列的if-return判断,最后终于到了错误处理地方了,不容易啊- -!
        std::cerr << " occur error from do_read_func_r." << endl;
    }
}

## 总结
综上示例,稍微总结一下,try-throw-catch结构的C++异常处理机制的优势地方
1. 便于集中处理不同的异常情况,尤其针对不同(多)逻辑层次的异常处理情况,但最好不要跨模块处理异常。
     针对不同(多)逻辑层次的异常处理,若不使用异常处理机制,则需要在错误(异常)出现地方地方一层层返回错误情况,直到得到处理,编写代码时负担累赘比较大。

2. 针对异常处理情况,其可扩展性比较好。
     如增加或减少一种异常的处理情况,涉及代码逻辑的修改比较小,只要取消最初的throw和catch处理对应情况。

3. 当发现错误情况,throw可立即中止该下面逻辑的执行,直至找到对应的catch情况,否则程序崩溃。
     既是优点也是缺点。

4. 免去手动编写很多的if-return逻辑,否则严重影响代码逻辑的清晰与美观,尤其对于那些具有代码洁癖的程序员。

5. 建议:在不影响程序运行性能的情况下,可考虑使用异常处理,尤其程序存在复杂的错误(异常)处理情况的。
     也就是说,复杂的错误处理逻辑,不是程序主要逻辑,即使使用了C++异常处理也不影响程序的整体性能。

## 猜想
猜想,但未进一步验证:
     C++异常机制的实现涉及到相应操作系统的异常处理机制,编译器自动转换为系统的异常处理逻辑,当然这里的转换逻辑一定很复杂。
以windows为例,C++异常处理的底层实现可能将涉及 SEH(结构化异常处理):
__try{}__except(…){}
__try{}__finally{}
还有,涉及系统函数UnHandleExceptionFilter,SetUnHandleExceptionFilter (详见《软件调试》)...
     PS. 至于Linux系统下,C++异常处理的底层实现日后有机会再考究。

## 附:

曾整理过另一篇博文慎用C++异常,主要是反对使用C++异常 。

涉及文章说明内容的完整示例代码,可以参见存在github


没有评论:

发表评论