Programming 版 (精华区)

发信人: godkiller (一天到晚游泳的鱼), 信区: Programming
标  题: C++ FAQ(12):赋值算符
发信站: 哈工大紫丁香 (2002年11月26日22:26:37 星期二), 站内信件

[12] 赋值算符
(Part of C++ FAQ Lite, Copyright ? 1991-2001, Marshall Cline, cline@parashift.
com)
简体中文版翻译:申旻,nicrosoft@sunistudio.com(东日制作室,东日文档)
------------------------------------------------------------------------------
--
FAQs in section [12]:
[12.1] 什么是“自赋值”? 
[12.2] 为什么应该当心“自赋值”? 
[12.3] 好,好;我会处理自赋值的。但如何做呢?  
------------------------------------------------------------------------------
--
[12.1] 什么是“自赋值”?
自赋值就是将对象赋值给本身。例如, 
 #include "Fred.hpp"    // 声明 Fred 类 
 
 void userCode(Fred& x)
 {
   x = x;   // 自赋值
 } 
很明显,以上代码进行了显式的自赋值。但既然多个指针或引用可以指向相同对象(别名
),那么进行了自赋值而自己却不知道的情况也是可能的: 
 
 #include "Fred.hpp"    // 声明 Fred 类
 
 void userCode(Fred& x, Fred& y)
 {
   x = y;   // 如果&x == &y就可能是自赋值
 }
 
 int main()
 {
   Fred z;
   userCode(z, z);
 } 
[ Top | Bottom | Previous section | Next section ] 
------------------------------------------------------------------------------
--
[12.2] 为什么应该当心“自赋值”?
如果不注意自赋值,将会使你的用户遭受非常微妙的并且一般来说非常严重的bug。例如,
如下的类在自赋值的情况下将导致灾难: 
 class Wilma { };
 
 class Fred {
 public:
   Fred()                : p_(new Wilma())      { }
   Fred(const Fred& f)   : p_(new Wilma(*f.p_)) { }
  ~Fred()                { delete p_; }
   Fred& operator= (const Fred& f)
     {
       // 差劲的代码:没有处理自赋值!
       delete p_;                // Line #1
       p_ = new Wilma(*f.p_);    // Line #2
       return *this;
     }
 private:
   Wilma* p_;
 }; 
如果有人将 Fred 对象赋给其本身,由于*this和 f 是同一个对象,line #1同时删除了t
his->p_和f.p_。而 line #2使用了已经不存在的对象*f.p_,这样很可能导致严重的灾难

作为 Fred 类的作者,你最起码有责任确信在Fred对象上自赋值是无害的。不要假设用户
不会在对象上这样做。如果对象由于自赋值而崩溃,那是你的过失。
另外:上述的Fred::operator= (const Fred&)还有第二个问题:如果在执行new Wilma(*
f.p_)时,抛出了异常(例如,内存不够的异常或者Wilma的拷贝构造函数中的异常), t
his->p_将成为悬空指针——它所指向的内存不再是可用的。这可以通过在删除就对象前创
建对象来解决。
[ Top | Bottom | Previous section | Next section ] 
------------------------------------------------------------------------------
--
[12.3] 好,好;我会处理自赋值的。但如何做呢? 
[Recently reworded the last paragraph (on 7/00). Click here to go to the next 
FAQ in the "chain" of recent changes.] 
在你创建类的每时每刻,都应该当心自赋值。这并不意味着需要为你所有的类都增加额外
的代码:只要对象优雅地处理自赋值,而不管是否必须增加额外的代码。 
如果不需要为赋值算符增加额外代码,这里有一个简单而有效的技巧: 
 Fred& Fred::operator= (const Fred& f)
 {
   if (this == &f) return *this;   // 优雅地处理自赋值
 
   // 此处写正常赋值的代码...
 
   return *this;
 } 
显式的测试并不总是必要的。例如,如果修正前一个 FAQ中的赋值算符使之处理new抛出的
异常和/或Wilma类的拷贝构造函数抛出的异常,可能会写出如下的代码。注意这段代码有
(令人高兴的)自动处理自赋值的附带效果:
 
 Fred& Fred::operator= (const Fred& f)
 {
   // 这段代码优雅地(但隐含的)处理自赋值
   Wilma* tmp = new Wilma(*f.p_);   // 如果异常在此处被抛出也没有问题
   delete p_;
   p_ = tmp;
   return *this;
 } 
在象这个例子的情况下(自赋值是无害的但是低效),一些程序员想通过增加另外的不必
要的测试,如“if (this == &f) return *this;”来改善自赋值时的效率。通常来说,使
自赋值情况更高效而使得非自赋值情况更低效的折衷是错误的。例如,为Fred类的赋值算
符增加如上的if测试会使得非自赋值情况更低效(一个额外的(而且不必要的)条件分支)
。如果自赋值实际上一千次才发生一次,那么 if 将浪费99.9%的时间周期。 
[ Top | Bottom | Previous section | Next section ] 
------------------------------------------------------------------------------
--
 E-mail the author
[ C++ FAQ Lite | Table of contents | Subject index | About the author | ? | Do
wnload your own copy ]
Revised Apr 8, 2001 

--
我不停的游啊游,游啊游,可怎么也游不到岸。

※ 来源:·哈工大紫丁香 bbs.hit.edu.cn·[FROM: 218.7.34.70]
[百宝箱] [返回首页] [上级目录] [根目录] [返回顶部] [刷新] [返回]
Powered by KBS BBS 2.0 (http://dev.kcn.cn)
页面执行时间:4.093毫秒