posts - 183,  comments - 10,  trackbacks - 0

swap 到底做了什么
swap 交换两个内置数据类型的变量时,直接交换。
swap 交换自定义类型对象时,如果里面没有成员指针,直接交换各个对应成员。
如果自定义类型中有指针成员,则是交换两个指针的值,但是指针的指向的值得不到交换。
正是由于这个原因,可以用 swap 进行重载 operator = 时避免自赋值情况,而是生产一个临时对象,然后与本对象 swap 即可。

关于重载 operator = 自赋值的情况,更详细内容可以查看《Effective C++》
实验程序:

  1 #include <iostream>
  2 using namespace std;
  3 
  4 class Str
  5 {
  6 private:
  7     char* s_;
  8 public:
  9     Str(const char* s = "")
 10     {
 11         s_ = new char[strlen(s) + 1];
 12         if (s_ == 0)
 13         {
 14             cout << "test" << endl;
 15             exit(1);
 16         }
 17         strcpy(s_, s);
 18     }
 19     // 定义拷贝构造函数,这里会被用于 operator =,swap
 20     Str(const Str& rhs)
 21     {
 22         s_ = new char[strlen(rhs.s_) + 1];
 23         if (s_ == 0)
 24         {
 25             cout << "test" << endl;
 26             exit(1);
 27         }
 28         strcpy(s_, rhs.s_);
 29     }
 30     ~Str()
 31     {
 32         clear();
 33     }
 34     //// 常规的 operator = 重载实现方式,必须检查自赋值
 35     //// 因为如果不自赋值检验,对于自赋值现象如果不调用 clear,则 s_ 在 new 之后就改变,rhs 也改变,原来的丢失,后来的也不是合法内容
 36     //// 如果调用 clear,不会内存泄露,但是 rhs 的内容被释放掉,rhs 的内容也不是合法内容。
 37     //// 如果检验自赋值,而没有 clear,原来 *this 的那块内存会被丢失,造成内存泄露。
 38     //Str& operator = (const Str& rhs)
 39     //{
 40     //    if (this != &rhs)
 41     //    {
 42     //        clear();
 43     //        s_ = new char[strlen(rhs.s_) + 1];
 44     //        if (s_ == 0)
 45     //        {
 46     //            exit(1);
 47     //        }
 48     //        strcpy(s_, rhs.s_);
 49     //    }
 50     //    return *this;
 51     //}
 52 
 53     // 改进的 operator,先用一个 temp 保持 rhs,然后 swap
 54     // 这种方式不怕自赋值,因为如果是自赋值,也有一个备份 temp,操作值相同的两个对象 *this 和 temp,直接交换不会影响结果
 55     // 如果不是自赋值,不是交换 *this 和 rhs,而是交换 *this 和 rhs 的一个复制品 temp,最终 *this 得到的值就是 rhs 的一个副本,完成赋值
 56     // 这种方式不用检验自赋值,所以可以省去每次调用时的自赋值检验,在基本上不会遇到自赋值检验的情况下,这种方法可以省去很多误用的检验
 57     // 但是它会每次生成一个副本,这样做的效率与原来的非自赋值一样,而且还需要一个 swap,但是这种方式是异常安全的,用对象来管理资源,资源分配即初始化
 58     Str& operator = (const Str& rhs)
 59     {
 60         cout << "test" << endl;
 61         Str temp(rhs);
 62         // swap(*this, temp);
 63         // 这里会引起递归调用,因为 operator = 调用 swap,swap 内部又调用 operator = ,一直递归下去,直到栈溢出
 64         swap(s_, temp.s_);
 65         // Effective C++ 中提到,可以定义一个成员函数 swap,用于交换两个对象对应的数据成员。这样可以防止无限递归。
 66         // 另一种好的方式是除定义一个成员函数 swap 外,传参类型为 值类型 T,这样就可以直接交换返回。
 67         // 这些方法的前提都是要有定义拷贝构造函数的。
 68         return *this;
 69     }
 70 
 71     void clear()
 72     {
 73         delete [] s_;
 74     }
 75     void foo()
 76     {
 77         cout << s_ << endl;
 78     }
 79 };
 80 
 81 int main()
 82 {
 83     int a = 3, b = 5;
 84     swap(a, b);
 85     cout << a << endl;
 86     cout << b << endl;
 87 
 88     Str s1("abc");
 89     Str s2("xyz");
 90     s1.foo();
 91     s2.foo();
 92 
 93     swap(s1, s2);
 94     // 这里输出两个 test,我们得知,有两个赋值操作
 95     // 可以推测 swap 的内部实现是 T t(s2), s2 = s1, s1 = t;
 96     s1.foo();
 97     s2.foo();
 98 
 99     s2 = s1;
100     s1.foo();
101     s2.foo();
102 
103     return 0;
104 }


posted on 2011-05-27 22:14 unixfy 阅读(871) 评论(0)  编辑 收藏 引用

只有注册用户登录后才能发表评论。
网站导航: 博客园   IT新闻   BlogJava   博问   Chat2DB   管理