Guru of the Week 条款11:对象等同(Object Identity)问题

类别:编程语言 点击:0 评论:0 推荐:

GotW #11 Object Identity

著者:Herb Sutter     

翻译:kingofark

[声明]:本文内容取自www.gotw.ca网站上的Guru of the Week栏目,其著作权归原著者本人所有。译者kingofark在未经原著者本人同意的情况下翻译本文。本翻译内容仅供自学和参考用,请所有阅读过本文的人不要擅自转载、传播本翻译内容;下载本翻译内容的人请在阅读浏览后,立即删除其备份。译者kingofark对违反上述两条原则的人不负任何责任。特此声明。

Revision 1.0

Guru of the Week 条款11:对象等同(Object Identity)问题

难度:5 / 10

(“我到底是谁?”这个问题蕴含着如何确定两个指针是否真的指向同一个对象的问题)

 

[问题]

测试语句“this != &other” 是一个防止自赋值(self-assingment)的常见编码手法。那么,要到达这个“防止自赋值(self-assingment)”目的,现有的语句是否是必要的和/或充分的呢?为什么?或者为什么不?如果答案是否定的,你又如何进行修改呢?注意要区分“protecting against Murphy vs. protecting against Machiavelli”。(* 译注:见本条款末尾)

    T& T::operator=( const T& other ){

        if( this != &other ) {  // the test in question

            // ...

        }

        return *this;

    }

 

[解答]

一个简短的解答:从技术上来看,它既不是必要的也不是充分的。在实践当中,它工作得颇好但也有可能在C++标准中被修改。

 

 * 论点:异常-安全(Murphy

     如果operator=()是异常-安全的(exception-safe),那么你并不需要检查自赋值(self-assignment)。这里有两个效率上的不利因素:a)如果可以进行自赋值(self-assignment)检查的话,那就可以进行彻底的优化从而省略掉赋值操作;b)如果代码被写成异常-安全的(exception-safe),那么同时也使得代码损失了一部分效率(这也即是说“paranoia has a price principle”,意即“偏执狂也有权衡代价的原则”)。

 

* 不是论点的论点:多重继承

过去曾有人把多重继承与本条款讨论的问题联系起来,但实际上这个问题与多重继承没有任何关系。我们的讨论的是一个涉及到C++标准怎样让你比较两个指针的技术性问题。

 

* 论点:运算符的重载(Machiavelli

虽然一些类可能会提供它们自己的operator&(),但是问题中对自赋值(self-assignment)的检查却很可能不如你所期望的那样运作,而是做一些完全不同的事情。用“protecting against Machiavelli”来意寓这种情况是因为,我们只能推测编写operator=()的人也许大概知道他实现的类是不是也重载了operator&()。

要注意,一个类可能也会提供一个T::operator!=(),但这与本问题无关——它并不影响我们对自赋值(self-assignment)的检查,原因是:由于一个被重载的运算符至少有一个参数的类型必须是“类(class)”类型,因而我们不可能编写一个包含有两个T*型参数的T::operator!=()。

 

后记1

下面是一个所谓的“笑话代码(code joke)”。信不信由你,竟然真有一些并无恶意但无疑是被误导了的代码编写者曾企图使用这样的代码:

 T::T( const T& other ) {

   if( this != &other ) {

      // ...

   }

}

 怎么样?你能第一眼就看出毛病吗?

 

后记2

       值得注意的是,还有另外一些情况,在这些情况当中,指针的比较也不是多数人光凭直觉就能考虑周到的。比如:

1.  James Kanze指出,把指针比较成字符串的行为是未定义的。其原因(我还未见有人给出过)是C++标准明确规定:允许编译器将字符串存放在重叠的内存区域以作为一种空间优化方案。

2.  一般来说,虽然像诸如<,<=,>和>=等内建(built-in)运算符的运算结果在各种特定情况下(比如,同一个数组中的两个指向对象的指针)都有着良好的定义,但你还是不能用这些运算符对任意的指针进行比较。标准库的使用也是基于这种限制的,其中规定:less<>以及其它库函数必须给出各指针的次序(ordering),以使得我们可以创建,比方说,一个key为指针类型的map,即map<T*,U,less<T*>>。(译注:此处的map是STL的一部分,一个map包括key和value两部分,使用的时候需要#include <map>)

 

(* 译注:关于Murphy和Machiavelli,侯捷先生在其系列书评的《C++/OOP大系》中提到:“就我的英文程度而言,[Sutter99](即《Exceptional C++》)读起来不若[Meyers96] (即《More Effective C++》)和[Meyers98] (即《Effective C++2/e》)那般平顺,原因是其中用了很多厘语、口语、典故。举个例子,Morphy law是什麽,大家知道吗?(莫菲定律说:会出错的,一定会出错。)Machiavelli又代表了什麽意思?(意大利政治家,以诈术闻名。)”

在这里,本条款的译者kingofark只能惭愧的说kingofark自己也没有完全理解本条款使用Murphy和Machiavelli两个词的用意。

Machiavelli:马基雅维利,尼克尔1469-1527意大利政治理论家,他的著作 君主论(1513年)阐述了一个意志坚定的统治者不顾道德观念的约束如何获得并保持其权力;马基雅弗利,意大利新兴资产阶级思想政治家,历史学家。)

本文地址:http://com.8s8s.com/it/it29899.htm