阅读 148

VC++6.0升级到VC++2010理由之一(符合C++标准编写更加安全的代码)

  VC++6.0升级到VC++2010理由之一(符合C++标准编写更加安全的代码)    


VC++6.0升级的一个很大的障碍就是代码不兼容,但是VC++6.0的C++不够标准,不够安全,存在诸多安全漏洞,内存析出,缓冲区溢出,等等,代码的安全性,可靠性得不到有利的保障,为了您的软件的长治久安,以及在Windows7平台上的迁移,请克服代码兼容性带来的麻烦,一定要设法升级到VC++2010。

关于C++中异常的争论何其多也,但往往是一些不合事实的误解。异常曾经是一个难以用好的语言特性,幸运的是,随着C++经验的积累,今天我们已经有足够的知识轻松编写异常安全的代码了,而且编写异常安全的代码一般也不会对性能造成影响。
    使用异常还是返回错误码?这是个争论不休的话题。大家一定听说过这样的说法:只有在真正异常的时候,才使用异常。那什么是“真正异常的时候”?在回答这个问题以前,让我们先看一看程序设计中的不变式原理。
    对象就是属性聚合加方法,如何判定一个对象的属性聚合是不是处于逻辑上正确的状态呢?这可以通过一系列的断言,最后下一个结论说:这个对象的属性聚合逻辑上是正确的或者是有问题的。这些断言就是衡量对象属性聚合对错的不变式。
    我们通常在函数调用中,实施不变式的检查。不变式分为三类:前条件,后条件和不变式。前条件是指在函数调用之前,必须满足的逻辑条件,后条件是函数调用后必须满足的逻辑条件,不变式则是整个函数执行中都必须满足的条件。在我们的讨论中,不变式既是前条件又是后条件。前条件是必须满足的,如果不满足,那就是程序逻辑错误,后条件则不一定。现在,我们可以用不变式来严格定义异常状况了:满足前条件,但是无法满足后条件,即为异常状况。当且仅当发生异常状况时,才抛出异常。
    关于何时抛出异常的回答中,并不排斥返回值报告错误,而且这两者是正交的。然而,从我们经验上来说,完全可以在这两者中加以选择,这又是为什么呢?事实上,当我们做出这种选择时,必然意味着接口语意的改变,在不改变接口的情况下,其实是无法选择的(试试看,用返回值处理构造函数中的错误)。通过不变式区别出正常和异常状况,还可以更好地提炼接口。
    对于异常安全的评定,可分为三个级别:基本保证、强保证和不会失败。
基本保证:确保出现异常时程序(对象)处于未知但有效的状态。所谓有效,即对象的不变式检查全部通过。
强保证:确保操作的事务性,要么成功,程序处于目标状态,要么不发生改变。
不会失败:对于大多数函数来说,这是很难保证的。对于C++程序,至少析构函数、释放函数和swap函数要确保不会失败,这是编写异常安全代码的基础。
    首先从异常情况下资源管理的问题开始.很多人可能都这么干过:
    Type* obj = new Type;
    try{  do_something...}
    catch(...){ delete obj; throw;}
    不要这么做!这么做只会使你的代码看上去混乱,而且会降低效率,这也是一直以来异常名声不大好的原因之一. 请借助于RAII技术来完成这样的工作:
    auto_ptr

    强保证是事务性的,这个事务性和数据库的事务性有区别,也有共通性。实现强保证的原则做法是:在可能失败的过程中计算出对象的目标状态,但是不修改对象,在决不失败的过程中,把对象替换到目标状态。考察一个不安全的字符串赋值方法:
string& operator=(const string& rsh){
    if (this != &rsh){
        myalloc locked_pool(m_data);
        locked_pool.deallocate(m_data);
        if (rsh.empty())
        m_data = NULL;
        else{
        m_data = locked_pool.allocate(rsh.size() + 1);
        never_failed_copy(m_data, rsh.m_data, rsh.size() + 1);
        }
    }
    return *this;
    }
locked_pool是为了锁定内存页。为了讨论的简单起见,我们假设只有locked_pool构造函数和allocate是可能抛出异常的,那么这段代码连基本保证也没有做到。若allocate失败,则m_data取值将是非法的。参考上面的b条目,我们可以这样修改代码:
myalloc locked_pool(m_data);
    locked_pool.deallocate(m_data);   //进入非法状态
    m_data = NULL;            //立刻再次回到合法状态,且不会失败
    if(!rsh.empty()){
    m_data = locked_pool.allocate(rsh.size() + 1);
    never_failed_memcopy(m_data, rsh.m_data, rsh.size() + 1);
    }
现在,如果locked_pool失败,对象不发生改变。如果allocate失败,对象是一个空字符串,这既不是初始状态,也不是我们预期的目标状态,但它是一个合法状态。我们阐明了实现基本保证所需要的技巧部分,结合前述的基础设施(RAII的运用),完全可以实现基本保证了...哦,其实还是有一点疏漏,不过,那就留到最后吧。
   继续,让上面的代码实现强保证:
myalloc locked_pool(m_data);
    char* tmp = NULL;
    if(!rsh.empty()){
    tmp = locked_pool.allocate(rsh.size() + 1);
    never_failed_memcopy(tmp, rsh.m_data, rsh.size() + 1); //先生成目标状态
    }
    swap(tmp, m_data);       //对象安全进入目标状态
    m_alloc.deallocate(tmp);    //释放原有资源
强保证的代码多使用了一个局部变量tmp,先计算出目标状态放在tmp中,然后在安全进入目标状态,这个过程我们并没有损失什么东西(代码清晰性,性能等等)。看上去,实现强保证并不比基本保证困难多少,一般而言,也确实如此。不过,别太自信,举一种典型的很难实现强保证的例子,对于区间操作的强保证:
    for (itr = range.begin(); itr != range.end(); ++itr){
    itr->do_something();
    }
如果某个do_something失败了,range将处于什么状态?这段代码仍然做到了基本保证,但不是强保证的,根据实现强保证的基本原则,我们可以这么做:
    tmp = range;
    for (itr = tmp.begin(); itr != tmp.end(); ++itr){
    itr->do_something();
    }
    swap(tmp, range);
似乎很简单啊!呵呵,这样的做法并非不可取,只是有时候行不通。因为我们额外付出了性能的代价,而且,这个代价可能很大。无论如何,我们阐述了实现强保证的方法,怎么取舍则由您决定了。

    接下来讨论最后一种异常安全保证:不会失败。
    通常,我们并不需要这么强的安全保证,但是我们至少必须保证三类过程不会失败:析构函数,释放类函数,swap。析构和释放函数不会失败,这是RAII技术有效的基石,swap不会失败,是为了“在决不失败的过程中,把对象替换到目标状态”。我们前面的所有讨论都是建立在这三类过程不会失败的基础上的,在这里,弥补了上面的那个疏漏。
    一般而言,语言内部类型的赋值、取地址等运算是不会发生异常的,上述三类过程逻辑上也是不会发生异常的。内部运算中,除法运算可能抛出异常。但是地址访问错通常是一种错误,而不是异常,我们本应该在前条件检查中就发现的这一点的。所有不会发生异常操作的简单累加,仍然不会导致异常。

好了,现在我们可以总结一下编写异常安全代码的几条准则了:
1.只在应该使用异常的地方抛出异常
2.如果不知道如何处理异常,请不要捕获(截留)异常。
3.充分使用RAII,旁路异常。
4.努力实现强保证,至少实现基本保证。
5.确保析构函数、释放类函数和swap不会失败。

另外,还有一些语言细节问题,因为和这个主题有关也一并列出:
1.不要这样抛出异常:throw new exception;这将导致内存泄漏。
2.自定义类型,应该捕获异常的引用类型:catch(exception& e)或catch(const exception& e)。
3.不要使用异常规范,即使是空异常规范。编译器并不保证只抛出异常规范允许的异常,更多内容请参考相关书籍




©著作权归作者所有:来自51CTO博客作者yinchengmvp的原创作品,如需转载,请注明出处,否则将追究法律责任


文章分类
后端
版权声明:本站是系统测试站点,无实际运营。本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 XXXXXXo@163.com 举报,一经查实,本站将立刻删除。
相关推荐