zoukankan      html  css  js  c++  java
  • Effective C++_笔记_条款11_在operator=中处理“自我赋值”

    (整理自Effctive C++,转载请注明。整理者:华科小涛@http://www.cnblogs.com/hust-ghtao/) 

        为什么会出现自我赋值呢?不明显的自我赋值,是“别名”带来的结果:所谓“别名”就是“有一个以上的方法指涉对象”。一般而言如果某段代码操作pointers或references而它们被用来“指向多个相同类型的对象”,就需要考虑这些对象是否为同一个。实际上两个对象来自同一个继承体系,它们甚至不需要声明为相同类型就可能造成“别名”。因为一个base class的reference或pointer可以指向一个derived class对象。如果你尝试自行管理资源,可能会掉进“在停止使用资源之前意外释放了它”的陷阱。假设你建立一个class用来保存一个指针指向一块动态分配的位图(bitmap):

       1: class Bitmap{...};
       2: class Widget{
       3:     ...
       4:     private:
       5:         Bitmap* pb ;
       6: };

        下面是operator=的实现代码:

       1: Widget&
       2: Widget::operator=(const Widget& rhs)
       3: {
       4:     delete pb ;                  //停止使用当前的bitmap
       5:     pb = new Bitmap (*rhs.pb) ;  //使用rhs的bitmap的副本
       6:     return *this ;                //返回当前对象的引用
       7: }

        这份代码表面上合理,但自我赋值时并不安全。operator=函数内的*this(赋值的目的端)和rhs有可能是同一个对象。果真如此delete就不只销毁当前对象的bitmap,它也销毁rhs的bitmap。在函数的末尾,Widget—它原本不该被自我赋值动作改变的—发现自己持有一个指针指向一个已被删除的对象。

        欲阻止这种错误,可以在operator=最前面增加一个“正同测试”(identity test)达到“自我赋值”的检验目的:

       1: Widget& Widget::operator=(const Widget& rhs)
       2: {
       3:     if(this==&rhs) return *this ;      //正同测试:如果是自我赋值,直接返回当前对象
       4:     
       5:     delete pb ;
       6:     pb = new Bitmap( *rhs.pb );
       7:     return *this ;
       8: }

        这个版本具备“自我赋值安全性”,但是仍然存在异常方面的麻烦。更明确地说,如果“new Bitmap”导致异常,Widget最终会持有一个指针指向一块被删除的Bitmap。

        令人高兴的是,让operator=具备“异常安全性”往往自动获得“自我赋值安全性”的回报。许多时候一群精心安排的语句就可以导出异常安全的代码。例如以下代码,我们只需注意在赋值pb之前别删除pb:

       1: Widget& Widget::operator=(const Widget& rhs)
       2: {
       3:     Bitmap* pOrig = pb ;              //记住原先的pb
       4:     pb = new Bitmap( *rhs.pb ) ;      //令pb指向*pb的一个复件
       5:     delete pOrig ;                    //删除原来的pb
       6:     return *this ; 
       7: }

    现在,如果“new Bitmap”抛出异常,pb保持原状。

        在operator=函数内手工排列语句的一个替代方案是,使用所谓的copy和swap技术:

       1: class Widget{
       2: ...
       3: void swap(Widget& rhs);       //交换*this和rhs的数据
       4: ...
       5: };
       6: Widget& Widget::operator=(const Widget& rhs)
       7: {
       8:     Widget temp(rhs);  //为rhs的数据制作一个副本
       9:     swap(temp);        //将*this数据和上述复件的数据交换
      10:     return *this;
      11: }

        请注意:

    (1)确保当对象自我赋值时operator=有良好行为。其技术包括比较“来源对象”和“目标对象”的地址、精心周到的语句顺序、以及copy-and-swap。

    (2)确定任何函数如果操作一个以上的对象,而其中多个对象是同一个对象时,其行为仍然正确。

  • 相关阅读:
    发现个atan2的正确使用方式
    Forward+ Shading架构
    fatal: unable to connect to gitee.com: gitee.com[0: 180.97.125.228]: errno=Unknown error 解决方案
    HDFS HA(高可用性)集群规划
    如何使用RTP引擎对语音编码进行转码
    关于 Angular 应用 tsconfig.json 中的 target 属性
    浅谈 Orbeon form builder 的权限控制
    关于 Angular 应用 tsconfig.json 中的 lib 属性
    orbeon form 通过 url 的方式同第三方应用集成的开发明细
    orbeon form 的配置介绍
  • 原文地址:https://www.cnblogs.com/hust-ghtao/p/3814809.html
Copyright © 2011-2022 走看看