在前面的认知里,空指针是不能访问有用的数据和函数的,因为空指针并没有指向什么有意义的数据存储空间。但是在C++类对象中就不见得是这样了,C++类对象中,空指针可以访问成员函数。
我们看下面的代码及其运行情况:
#include
using namespace std;
class Person
{
public:
//定义Person类的一个成员函数
void ShowClassFunc()
{
cout << "这是一个 person类的成员函数\n";
}
};
void test()
{
Person p; //1.根据Person类创建一个对象p
Person* ptr = NULL; //2.创建一个Person类型的空指针
p.ShowClassFunc();//3.通过具体的对象p访问类成员函数
ptr->ShowClassFunc();//4.通过空指针访问类成员函数
}
int main()
{
//运行测试函数
test();
return 0;
}
运行结果:
这是一个 person类的成员函数
这是一个 person类的成员函数
我们可以发现,空指针ptr居然可以访问类成员函数ShowClassFunc(),这是为什么呢?为什么空指针能调用成员函数?
要搞清楚这个问题,我们首先复习我前一篇博客《7.C++this指针》开头的一段话:
每一个非静态成员函数只会诞生一份函数实例,也就是说多个同类型的对象会共用一块代码。
也就是说当我们的程序被编译之后,此成员函数在内存空间中地址即已确定。当调用:
ptr->ShowClassFunc();
这句话时,其实就是相当于是调用:
Person::ShowClassFunc();
但是!
如果代码修改为下面这样,结果还是这样吗?
#include
using namespace std;
class Person
{
public:
//定义Person类的一个非静态成员函数
void ShowClassFunc()
{
cout << "这是一个 person类的非静态成员函数\n";
m_A = 20;//成员函数修改非静态成员变量m_A
}
//定义成员变量m_A并初始化
int m_A = 10;
};
void test()
{
Person p; //1.根据Person类创建一个对象p
Person* ptr = NULL; //2.创建一个Person类型的空指针
p.ShowClassFunc();//3.通过具体的对象p访问类成员函数
ptr->ShowClassFunc();//4.通过空指针访问类成员函数
}
int main()
{
//运行测试函数
test();
return 0;
}
再次运行结果
Segmentation fault
直接报错了。
我们单独看两个成员函数的调用方式的允许情况:
//1.程序中只调用:
p.ShowClassFunc();
//运行结果:
这是一个 person类的成员函数
//2.程序中只调用:
ptr->ShowClassFunc();
//运行结果:
Segmentation fault
//3.程序中同时调用:
p.ShowClassFunc();
ptr->ShowClassFunc();
//运行结果:
Segmentation fault
我们可以发现问题出在 :
ptr->ShowClassFunc();
这里,为什么一开始还好好的,成员函数ShowClassFunc()中加入了修改非静态成员变量的语句:
m_A = 20;//成员函数修改非静态成员变量m_A
就报错了呢?
其实在我们的程序中,下面这段代码:
//定义Person类的一个非静态成员函数
void ShowClassFunc()
{
cout << "这是一个 person类的非静态成员函数\n";
m_A = 20;//成员函数修改非静态成员变量m_A
}
其实就是:
//定义Person类的一个非静态成员函数
void ShowClassFunc()
{
cout << "这是一个 person类的非静态成员函数\n";
this->m_A = 20;//成员函数修改非静态成员变量m_A
}
程序中:
Person* ptr = NULL; //2.创建一个Person类型的空指针
可以怎这么理解:空指针ptr指向为空,没有指向一个对象实体,由于每一个对象实体都会有一个this指针指向该对象本身,所以当我们第一次使用ptr访问成员函数ShowClassFunc(),函数体内没有用到this指针,所以不会出现问题。
但是第二次程序中成员函数ShowClassFunc()体内用到this指针时,由于此时ptr并没有指向具体的对象实体,所以this指向空,当进行赋值的时候,编译器不知道这个成员变量是哪一个对象的(不知道this->m_A = 20是哪个对象在调用),所以它不知道给哪个对象的m_A赋值,因此就会出错。
所以我们可以总结出如下信息:
类对象空指针是可以调用普通成员函数,但前提是看该函数体中是否使用到了this指针,即是否访问非静态成员变量。如果使用到了this指针,程序会报错;如果没有使用到this指针,程序是可以正常运行的。
那如果使用类对象空指针访问静态成员函数中的静态变量会怎么样呢?
答案是没问题,由于静态成员函数内部没有this指针,同时静态成员函数只能访问静态成员变量,不能访问非静态成员变量,因此程序没有问题,示例如下:
#include
using namespace std;
class Person
{
public:
//定义Person类的一个静态成员函数
static void ShowClassFunc()
{
cout << "这是一个 person类的静态成员函数\n";
m_A = 20;//成员函数修改非静态成员变量m_A
}
//定义静态成员变量m_A
static int m_A;
};
int Person:: m_A = 10;//初始化静态成员变量m_A
void test()
{
Person p; //1.根据Person类创建一个对象p
Person* ptr = NULL; //2.创建一个Person类型的空指针
p.ShowClassFunc();//3.通过具体的对象p访问类的静态成员函数
ptr->ShowClassFunc();//4.通过空指针访问类的静态成员函数
}
int main()
{
//运行测试函数
test();
return 0;
}
运行结果:
这是一个 person类的静态成员函数
这是一个 person类的静态成员函数