Contents

今天看到一篇文章Still Comparing “this” Pointer to Null?,批评了判断this指针是不是null的做法。

文章中首先给出了如下的代码示例:

1
2
3
4
5
6
7
class CWindow {
HWND handle;
HWND GetSafeHandle() const
{
return this == 0 ? 0 : handle;
}
};

这段代码看起来很奇怪,貌似目的只是为了允许一个空的CWindow*指针调用GetSafeHandle方法。

但是我们不应该这么做,首先C++的标准说明通过一个空指针调用非静态函数是未定义的行为。不过如下的代码可能能正常运行。

1
2
3
4
5
6
7
8
9
10
11
12
13
class Class {
public:
void DontAccessMembers()
{
::Sleep(0);
}
};

int main()
{
Class* object = 0;
object->DontAccessMembers();
}

因为这个方法并没有使用任何成员变量,那么看起来它和静态函数差不多,所以调用可能是没问题的。

但是编译器可能会优化,比如最开头举得那个例子,可能编译器会认为this指针永远不会是null,这样就可能把判断this指针是不是空的判断优化掉。就像优化如下的if检查一样。

1
2
3
4
5
int divideBy = ...;
whatever = 3 / divideBy;
if( divideBy == 0 ) {
// THIS IS IMPOSSIBLE
}

所以,你需要确保你的编译器不会做这样的优化,当然现在还没有任何编译器做这样的优化,但是未来不好说:)

另外,你如果看看如下代码的输出,估计会疯掉了:)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
class FirstBase {
int firstBaseData;
};

class SecondBase {
public:
void Method()
{
if (this == 0) {
printf("this == 0\r\n");
}
else {
printf("this != 0 (value: %p)\r\n", this);
}
}
};

class Composed1 : public FirstBase, public SecondBase {
};

class Composed2 : public SecondBase, public FirstBase {
};


int main()
{
Composed1* object1 = 0;
object1->Method();

Composed2* object2 = 0;
object2->Method();

SecondBase* object3 = reinterpret_cast<Composed1*>(0);
object3->Method();

SecondBase* object4 = reinterpret_cast<Composed1*>(1);
object4->Method();
}

Visual Studio 2013的输出结果是:

1
2
3
4
5
this != 0 (value: 00000004)
this == 0
this == 0
this != 0 (value: 00000005)
Press any key to continue . . .

object1的this居然不是0,而是4,这是因为FirstBase有一个int的成员变量,在调用SecondBase的Method时,指针向后移了4!

object2的this就是0了,因为不需要向后移。

关于object3和object4的输出,就需要看看reinterpret_cast的汇编了。

1
2
3
4
5
6
7
8
9
10
SecondBase* object = reinterpret_cast<Composed1*>( rand() );
010C1000 call dword ptr [__imp__rand (10C209Ch)]
010C1006 test eax,eax
010C1008 je wmain+0Fh (10C100Fh)
010C100A add eax,4
object->Method();
010C100D jne wmain+20h (10C1020h)
010C100F push offset string "this == 0" (10C20F4h)
010C1014 call dword ptr [__imp__printf (10C20A4h)]
010C101A add esp,4

可以看到那个je指令,如果是0,就会jump,这样就不会加4,如果不是0,就会加4。所以object3的输出是0,object4的输出是5。彻底崩溃了吧。

所以总结一下,不要在非静态函数中判断this指针是不是null,如果想支持空指针也能调用这个函数,就把这个函数定义为静态的。

Contents