我最近遇到了以下代码:
class Foo
{
public:
void bar();
// .. other stuff
};
void Foo::bar()
{
if(!this) {
// .. do some stuff without accessing any data members
return;
}
// .. do normal actions using data members
}
代码编译因为在C ++方法中只是隐式传递'this'指针的函数,'this'可以像任何其他指针一样被检查为NULL。显然,即使它没有崩溃,这段代码也会让人感到困惑和不好。在调试器中单步调试代码会非常混乱,看到一个NULL指针即将有一个调用它的方法,然后看不到预期的崩溃。我的问题是:它是否违反了C ++标准来调用SomeFooPtr->bar()
SomeFooPtr == NULL
?
我觉得它可能不是因为用户定义的操作符 - >返回一个指针,这意味着即使该指针为NULL,它肯定没有被解除引用(取消引用一个NULL指针,我确信该标准被视为非法或未定义)。另一方面,原始指针的语义不一定必须与用户定义的指针的语义匹配 - 可能是operator->即使编译器不会生成一个,也将它们视为取消引用。
答案 0 :(得分:13)
这可能适用于大多数系统,但它是未定义的行为。标准:
5.2.5.3
如果
E1
的类型为“指向类X的指针”,则表达式E1->E2
将转换为等效形式(*(E1)).E2
[...]
和
5.2.5.1
后缀表达式,后跟点
.
或箭头->
,可选地后跟关键字template
(14.8.1),然后是 id-表达式,是一个后缀表达式。评估点或箭头之前的后缀表达式; 58) [...]58)即使结果不需要确定整个后缀表达式的值,也会发生此评估,例如,如果id-expression表示静态成员。
评估*x
其中x
是一个空指针会导致未定义的行为,所以在你输入函数之前,很明显是UB的情况。
答案 1 :(得分:7)
即使取消引用不是UB,此测试也会被破坏。当多重继承的调整进入游戏时,它会中断:
#include <stdio.h>
class B
{
int value;
public:
void foo()
{
if (!this)
printf("this==0\n");
else
printf("safe\n");
}
};
class A { public: int anotherValue; };
class Z : public A,public B {};
int main()
{
Z *z=0;
z->foo();
}
在这里打印“安全”。
答案 2 :(得分:1)
如果它是合法的并不重要,这对读者来说是混乱的。在此代码有效的实现中,vtable是按类型访问的,当然不是按对象访问。
此外,我希望这段代码可以用于覆盖构造函数失败,这会掩盖其他地方的各种问题。应该正确处理构造函数失败,而不是像示例那样令人讨厌的kludge。
答案 3 :(得分:1)
这是(现在一起)未定义的行为。但是,对于许多编译器来说,它将起作用,附加约束条件必须是非虚拟的。
答案 4 :(得分:1)
是UB。使其崩溃的一个好方法是将它用作使用多继承的派生类的基类。 YMMV。