关于对象的自我赋值行为

关于对象的自我赋值行为 



关于对象的自我赋值行为


自我赋值的语句,就像这样:



很明显,这是一段愚蠢的代码。但既然我们提到自我赋值会引发问题,那我们先来澄清一下自我赋值的情况其实有时并不是那么显而易见的,并不一定都像上述代码那么愚蠢,它们还可能是这样:




以上代码中,a[i] 和 a[j] 有可能是同一对象,两个不同的指针 px 和 py 有可能指向同一对象,而基类引用 r 也完全有可能引用了指针 p 所指向的同一对象。它们这看起来,要比前面的代码隐蔽多了。

关于对象的自我赋值行为

下面来说说,为什么自我赋值会有危险。考虑一个储存了一张 Jpeg 图片数据的类:



下面是 Image 类的 operator=() 的实现代码,看起来合情合理:



但,如果 r 跟调用对象是同一对象时,那将意味着在执行 delete p 之时就已经将 r 的图像数据删除了,此时再去根据此数据 new 一个新对象将会引发错误。纠正这个错误也不难,只要加个简单的判断:




这的确解决了所谓 “” 问题,但随之而来还有另一个问题,那就是 “” 问题,假设程序在分配堆内存时,不巧发生了始料未及的错误,也就是 new 语句发生了异常,此时因为 原先对象的图像数据 p 已经被删除,因此这个赋值运算将会导致一个尴尬的结局:新的数据尚未被正常赋予,旧的数据已经被匆匆删除。


因此,我们还需要仔细打磨以上代码,可以将之修改为:


=this->p;

;


此时,如果 new 语句再次发生异常,将不会对任何数据造成影响,可以免除编写  代码。当然,如果恰巧确实发生了 自我赋值 事件,那么代码将会白白浪费时间创建了一个原图像的复制品,然后让指针指向新的复制品上。如果你很在乎这个事情,你可以将 自我检测 代码重新加到代码中,可是这又将增加程序的尺寸,引入了一个新的结构分支,prefetching、caching 和 pipelining 指令的效率都会被拖累。因此你需要权衡这二者中的利弊。

关于对象的自我赋值行为

总结:

  1. 编写 operator=() 函数时要格外注意操作数是否是同一对象。

  2. 需要格外注意会发生异常(尤其是堆内存申请的代码)的代码处,是否会导致程序逻辑的不一致性。

  3. 保证任何函数在同时操作多个对象时,哪怕有多个对象是同一对象的情况下也能正常执行。

     

获取 IT 编程技术入门指导

关于对象的自我赋值行为


关于对象的自我赋值行为