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)确定任何函数如果操作一个以上的对象,而其中多个对象是同一个对象时,其行为仍然正确。

  • 相关阅读:
    TCP/IP
    Socket通信
    Dubbo详解
    高并发详解
    P3-DataBase
    JAVA基础学习之路(十)this关键字
    [SHELL]输出目录下所有的可执行文件,批量创建用户
    JAVA基础学习之路(八)[1]String类的基本特点
    [MYSQL][2]索引
    [MYSQL][1]创建,修改,删除表
  • 原文地址:https://www.cnblogs.com/hust-ghtao/p/3814809.html
Copyright © 2011-2022 走看看