翻译《有关编程、重构及其他的终极问题?》——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诊断。
- 翻译《有关编程、重构及其他的终极问题?》——25.不要再用this指针和nullptr比较了
- 翻译《有关编程、重构及其他的终极问题?》——7.不要在循环中调用alloca()函数
- 翻译《有关编程、重构及其他的终极问题?》——22.不要使用#pragram warning(default-X)
- 翻译《有关编程、重构及其他的终极问题?》——26.潜伏的VARIANT_BOOL
- 翻译《有关编程、重构及其他的终极问题?》——前言
- 翻译《有关编程、重构及其他的终极问题?》——30.Visual C++和wprintf()函数
- 翻译《有关编程、重构及其他的终极问题?》——11.不要试图把尽量多的操作符放到一行代码里
- 翻译《有关编程、重构及其他的终极问题?》——28.如果你可以使用简单的函数就不要使用宏
- 翻译《有关编程、重构及其他的终极问题?》——6.当把一个指针明确的转换为整型时,请检查所有相关代码
- 翻译《有关编程、重构及其他的终极问题?》——5.使用工具去分析你的代码
- 翻译《有关编程、重构及其他的终极问题?》——10.避免使用多个小的#ifdef块
- 翻译《有关编程、重构及其他的终极问题?》——13.表格化的格式化
- 翻译《有关编程、重构及其他的终极问题?》——17.使用专门的函数清除专有数据
- 翻译《有关编程、重构及其他的终极问题?》——23.自动获取字符串的长度
- 翻译《有关编程、重构及其他的终极问题?》——27.狡猾的BSTR字符串
- 翻译《有关编程、重构及其他的终极问题?》——1. 别把编译器的事给做了
- 翻译《有关编程、重构及其他的终极问题?》——9.使用'-0'符号作为结尾标记
- 翻译《有关编程、重构及其他的终极问题?》——14.一个好的编译器和代码风格还不够
- 这学期的课程设计,用java写的词法分析器
- JVM底层又是如何实现synchronized的
- android之xml文件中特殊符号的引用
- iOS- 详解文本属性Attributes
- hadoop不同用户提交mr任务的时候存在某个用户报错
- 翻译《有关编程、重构及其他的终极问题?》——25.不要再用this指针和nullptr比较了
- struts2面试题
- 八皇后
- xcode8.*安装插件教程,亲测
- Day 3 网络协议
- UGUI Tab键切换InputField
- zynq SGI-核间中断
- 九度1014:排名
- Android中如何实现双击退出APP