C++中,空指针是不能够使用的,然而空对象指针有时候却能够调用成员函数。
先来看一下以下程序:
#include<iostream>#include<string.h>using namespace std;class A{public: static void f1(){ cout<<"f1"<<endl; } void f2(){ cout<<"f2"<<endl; } void f3(){ cout<<num<<endl; } virtual void f4() {cout<<"f4"<<endl; }public: int num;};int main(int argc,char* argv[]){ A* pa = NULL; pa->f1(); //正常 pa->f2(); //正常 pa->f3(); //错误,提示段错误 pa->f4(); //错误,提示段错误 return 0;}空指针对f1和f2的调用正常,对f3和f4的调用会出错,为什么呢?
要理解这个的话,成员函数其实可以认为是一个普通的函数,比如
class A{public: void func(int x) { cout<<"hello, A. x="<<x<<endl; }};在编译器看来,大概就长这个样子吧:void A_func(A* this, int x) { cout<<"hello, A. x="<<x<<endl; }你平时使用成员函数的时候,大概就是这样的:A a;a.func(2);其实在编译器看来,是这个样子的:A_func(&a, 2);如果这么说的话,也许你就理解了,为什么对象是NULL的时候还可以调成员函数:A *pa = NULL;pa->func(2);//在编译器看来就好像是 A_func(pa, 2);且pa==NULL ((A*)NULL)->func(2);//在编译器看来就好像是 A_func( ((A*)NULL), 2);上面的例子中func函数里并没有使用成员变量。考虑有成员变量并且在成员函数里使用的情况,就会不一样了:class A{PRivate: int y;public: void func(int x) { y = x; }};注意此时y是成员变量,编译器会自动给它加上this->,也就是:void A_func(A* this, int x) { this->y = x; }此时正常的情况就不用说了,说说用NULL对象指针调用成员函数的情况:A *pa = NULL;pa->func(2);//在编译器看来就好像是 A_func(pa, 2);且pa==NULL((A*)NULL)->func(2);//在编译器看来就好像是 A_func( ((A*)NULL), 2);//好吧我承认这段代码跟上面的一毛一样啦!此时程序会崩溃!为什么?因为this指针是NULL,而你访问了它的y变量!从上面的分析及程序测试可以看出:1)NULL对象指针可以调用成员函数2)通过对象调用成员函数,对象的指针会被传入函数中,指针名称为this3)NULL对象指针调用成员函数时,只要不访问此对象的成员变量,则程序正常运行4)NULL对象指针调用成员函数时,一旦访问此对象的成员变量,则程序崩溃经过上边的实验在来看一下文章开始的例子,分析一下原因:类的成员函数并不与具体对象绑定,所有的对象共用同一份成员函数体,当程序被编译后,成员函数的地址即已确定,这份共有的成员函数体之所以能够把不同对象的数据区分开来,靠的是隐式传递给成员函数的this指针,成员函数中对成员变量的访问都是转化成"this->数据成员"的方式。因此,从这一角度说,成员函数与普通函数一样,只是多了一个隐式参数,即指向对象的this指针。而类的静态成员函数只能访问静态成员变量,不能访问非静态成员变量,所以静态成员函数不需要指向对象的this指针作为隐式参数。有了上面的分析,就可以解释为什么空对象指针对f1, f2的调用成功,对f3的调用不成功。f1是静态成员函数,不需要this指针,所以即使pa是空指针,也不影响对f1正常调用。f2虽然需要传递隐式指针,但是函数体中并没有使用到这个隐式指针,也就是说没有通过这个隐式指针去使用非静态的成员变量,所以pa为空也不影响对f2的正常调用。f3就不一样了,因为函数中使用到了非静态的成员变量,对num的调用被转化成this->num,也就是pa->num,而pa是空指针,因此pa->num非法,对f3的调用出错。f4中并没有使用非静态成员变量,为什么调用也会出错呢,原因在于f4是虚函数,有虚函数的类会有一个成员变量,即虚表指针,当调用虚函数时,会使用虚表指针,对虚表指针的使用也是通过隐式指针使用的,因此对f4的调用也就会出错了。
新闻热点
疑难解答