翻译《有关编程、重构及其他的终极问题?》——25.不要再用this指针和nullptr比较了

来源:互联网 发布:网站前台设计软件 编辑:程序博客网 时间:2024/05/25 21:32

翻译《有关编程、重构及其他的终极问题?》——25.不要再用this指针和nullptr比较了

标签(空格分隔): 翻译 技术 C/C++
作者:Andrey Karpov
翻译者:顾笑群 - Rafael Gu
最后更新:2017年05月22日


25.不要再用this指针和nullptr比较了

这段代码摘自CoreCLR项目。PVS-Studio对这段危险的代码诊断如下:V704 ‘this == nullptr’ expression should be avoided - this expression is always false on newer compilers, because ‘this’ pointer can never be NULL(译者注:大意是应该避免“this == nullptr”这种比较,因为“this”指针不可能为NULL,所以这个表达式始终返回false)。

bool FieldSeqNode::IsFirstElemFieldSeq(){  if (this == nullptr)    return false;  return m_fieldHnd == FieldSeqStore::FirstElemPseudoField;}

解释
过去大家经常用this指针和0/NULL/nullptr进行比较,在早期的C++发展阶段,这种方式是比较常见的情况,我们已经在“考古”的研究中(译者注:作者把对老代码的研究比喻成考古)常发生这种情况,关于这些,我建议大家可以从这篇关于checking Cfront的文章中读到,因为在那些日子里,this指针的值是可以被改变的,但因为太久远了,我们都忘了这点。

让我们回到用this指针和nullptr比较中。

现在,这种比较是不合法的,因为根据现代的C++标准,this指针是不可能等于nullptr的。

根据正式的C++标准,对于this指针为空指针的方法IsFirstElemFieldSeq()的调用会导致不可预知的行为。

看上去如果this == 0,那么当这个方法被执行的时候,就无法对这个类的字段进行访问了。但在实际中有两个可行的、但不太好的方法可以让这些代码得到执行。根据C++标准,this指针不可能为null,所以编译器可以优化对这个方法的调用,简化成如下的方式:

bool FieldSeqNode::IsFirstElemFieldSeq(){  return m_fieldHnd == FieldSeqStore::FirstElemPseudoField;}

顺便说一下,这里有个陷阱,假设我们有如下的继承结构:

class X: public Y, public FieldSeqNode { .... };....X * nullX = NULL;X->IsFirstElemFieldSeq();

假设Y的类大小为8个字节,然后对于NULL(0x00000000)的原指针,将被矫正为指向FieldSeqNode子对象实例的开始,这样,你就不得不使之偏移sizeof(Y)个字节。这样的话,在IsFirstElemFieldSeq()函数中的this值就被矫正为0x00000008了,那么“this == 0”检查就彻底失去了其意义。

正确的代码
实际上很难给出一个正确代码的示例,只是仅仅从函数中移除判断条件是不够的。你必须进行代码重构来保证从来不会用空指针来调用这个函数。

建议
所以,现在“if (this == nullptr)”虽然过时了,但你还是能在很多应用和库中经常看到类似的代码(MFC库就是一个例子)。这也就是为何Visual C++还是依旧努力的在用this指针和0比较。我猜这是因为编译器开发者还没有疯狂到要把已经正常工作了10多年的代码移除掉。

但规则已经确定了,所以让我们开始避免用this指针和null比较吧。并且,一旦你有空闲时间,检查所有的不合法比较并且重写它们是非常有用的。

编译器在很大程度上应该按照下面的方式工作:开始它们会给出这些比较的警告作为过渡(也许现在已经这么做了,我没有研究过这个问题);然后在某一时刻他们将会全面的支持新标准,这样你的代码(译者注:如果有前述比较的话)就会一起停止工作。所以我强烈建议你开始遵照新的规定,这在以后会很有帮助。

P.S. 当重构时,你也许需要Null object patter。

关于这个话题额外的链接:
1. 仍然在使用this指针和Null比较?
2. V704诊断。

阅读全文
0 0