看看下面的例子:
#include <
iostream>
using namespace std;
class MyClass{
public:
void Foo();
private:
int m_data;
};
void MyClass::Foo()
{
cout<<"MyClass::Foo() is invoked!"<<endl;
}
int main()
{
MyClass* ptr;
ptr = new MyClass;
delete ptr;
ptr = NULL;
ptr->Foo();
return 0;
}
编译没有问题,运行也没有问题,结果"MyClass::Foo() is invoked" 能打印出来.
奇怪的是ptr已经被设置成了NULL, 为什么ptr->Foo() 没有任何问题呢?
为什么编译和运行时都不能检测到这个问题呢. 为什么一个空指针还能够调用对象的成员函数呢? 我们要看看汇编代码.
MyClass::Foo() 其实是一个全局函数, 在符号表中的名字是 "_ZN7MyClass2FooEv". 所以调用时并不需要通过对象指针. 不过在调用这个函数时,会传递对象指针进去.
函数的定义:
.globl _ZN7MyClass3FooEv
.type _ZN7MyClass3FooEv, @function
_ZN7MyClass3FooEv:
.LFB1442:
pushl %ebp
.LCFI4:
movl %esp, %ebp
.LCFI5:
subl $8, %esp
.LCFI6:
subl $8, %esp
pushl $_ZSt4endlIcSt11char_traitsIcEERSt13basic_ostreamIT_T0_ES6_
subl $12, %esp
pushl $.LC0
pushl $_ZSt4cout
.LCFI7:
call _ZStlsISt11char_traitsIcEERSt13basic_ostreamIcT_ES5_PKc
addl $20, %esp
pushl %eax
.LCFI8:
call _ZNSolsEPFRSoS_E
addl $16, %esp
leave
ret
函数的调用:
ptr = NULL;
ptr->Foo();
return 0;
对应的汇编
movl $0, -4(%ebp) ;; ptr = NULL;
subl $12, %esp ;; 通过堆栈进行参数传递,传入ptr. 不过Foo()没有用到它.
pushl -4(%ebp) ;;
call _ZN7MyClass3FooEv ;; ptr->Foo()
addl $16, %esp
movl $0, %eax ;; return 0
leave
ret
通过上面的分析,我们明白了为什么这个让人迷惑的语句可以通过编译和运行了.
如果Foo()访问对象的成员,那么肯定是会出错的. 因为Foo()需要使用传入的ptr,而ptr的值是NULL.
比如
void Foo()
{
cout<<"MyClass::Foo() is invoked!"<<m_data<<endl;