场景1:
话不多说, 先看代码:
- main.cpp
#include <stdio.h>
#include "classA.h"
ClassA *pA = NULL;
int main()
{
pA->func1(); //成功输出"hello world"
pA->func2(); //调用了类成员函数,程序崩溃
return 0;
}
- classA.h
#include <stdio.h>
class ClassA {
public:
ClassA();
void func1();
void func2();
private:
int data;
};
- classA.cpp
#include "classA.h"
ClassA::ClassA(){}
void ClassA::func1()
{
printf("hello world\n");
}
void ClassA::func2()
{
data = 10; //空指针的调用于此处崩溃
printf("data:%d\n", data);
}
Linux编译和运行(win上IDE请忽略)
编译:
g++ main.cpp classA.cpp -o testA
运行:
./testA
运行结果
讨论
从上面运行结果可以看到:c++类对象为空指针时,可以调用未使用类成员变量的成员函数(func1);但如果成员函数(func2)有使用类成员变量时,则调用该函数会导致程序崩溃。
为什么会产生以上结果可以自行百度一下,相关的博客有很多,这里就不做解释了。
场景2:
以上展示了类对象空指针访问类成员函数的现象,下面讲一下利用类的静态变量实现伪空指针同样的调用,能成功调用操作类成员变量的成员函数而不会产生崩溃。
直接上代码:
- main.cpp
#include <stdio.h>
#include "classB.h"
ClassB *pB = NULL;
int main()
{
pB->func1(); //成功输出"hello world"
pB->func2(); //成功输出"data:10"
return 0;
}
- classB.h
#include <stdio.h>
class ClassB {
public:
ClassB();
void func1();
void func2();
private:
static ClassB obj;
int data;
};
- classB.cpp
#include "classB.h"
extern ClassB *pB;
ClassB ClassB::obj;
ClassB::ClassB()
{
pB = this;
}
void ClassB::func1()
{
printf("hello world\n");
}
void ClassB::func2()
{
data = 10;
printf("data:%d\n", data);
}
Linux编译和运行(win上IDE请忽略)
编译:
g++ main.cpp classB.cpp -o testB
运行:
./testB
运行结果
讨论
本处和场景1上的main.cpp基本上是一致的,但程序的空指针不仅能够成功调用未使用类成员变量的成员函数(func1),也能在调用有使用类成员变量成员函数(func2)而不至于崩溃。
能出现这个结果的原因就是main函数中的指针实际上是伪空指针,其实现主要归功于一个C关键字——extern 和类的静态成员;在类ClassB中,通过添加一个静态类成员然后利用其初始巧妙的将this指针传递给了全局变量pB,从而使pB有指向明确的地址而不是空指针;这也就是称它伪空指针的原因。
另外, 如果将类的构造函数和拷贝构造函数私有,可以不需要设置静态成员函数来获取实例就实现了全局仅有一个实例的单例模式。