C++核心编程:P6->类和对象----C++对象模型和this指针

本系列文章为黑马程序员C++教程学习笔记,前面的系列文章链接如下
C++核心编程:P1->程序的内存模型
C++核心编程:P2->引用
C++核心编程:P3->函数提高
C++核心编程:P4->类和对象----封装
C++核心编程:P5->类和对象----对象的初始化和清理

文章目录

  • 一、成员变量和成员函数分开存储
  • 二、this指针
  • 三、空指针访问成员函数
  • 四、const修饰成员函数


一、成员变量和成员函数分开存储

在C++中,类内的成员变量和成员函数分开存储,只有非静态成员变量才属于类的对象上。


我们先写一个空类,然后创建出一个对象,看看这个空对象占多大空间。

#include 
using namespace std;
#include 

class Person
{

};

void test01()
{
	Person p;
	cout << "size of p is: " << sizeof(p) << endl;
}

int main()
{
	test01();
	return 0;
}

运行,可以看到空对象大小为 1 1 1 字节。这是因为编译器为了区分空对象占内存的位置,给每个空对象也分配一个独一无二的内存地址。
C++核心编程:P6->类和对象----C++对象模型和this指针_第1张图片


当类中有成员变量时,看可能会占多大空间。我们这里在类中添加一个int类型的成员变量m_A。

#include 
using namespace std;
#include 

class Person
{
	int m_A;
};

void test01()
{
	Person p;
	cout << "size of p is: " << sizeof(p) << endl;
}

int main()
{
	test01();

	return 0;
}

可以看到类对象的大小是4字节。
C++核心编程:P6->类和对象----C++对象模型和this指针_第2张图片
若包含了静态成员变量,则它不属于类对象,类型对象的大小不变
C++核心编程:P6->类和对象----C++对象模型和this指针_第3张图片
当我们添加静态和非静态成员函数时,类对象大小也不变,因为它们也不属于类对象上。
C++核心编程:P6->类和对象----C++对象模型和this指针_第4张图片


二、this指针

问题

在C++中成员变量和成员函数是分开存储的,每一个非静态成员函数只会诞生一份函数实例,也就是说多个同类型的对象会共用一块代码。那么问题是:这一块代码是如何区分那个对象调用自己的呢?

this指针

c++通过提供特殊的对象指针,this指针,解决上述问题。this指针指向被调用的成员函数所属的对象。this指针是隐含每一个非静态成员函数内的一种指针。this指针不需要定义,直接使用即可。
this指针的用途:
----当形参和成员变量同名时,可用this指针来区分
----在类的非静态成员函数中返回对象本身,可使用return *this


当形参和成员变量同名时,不能直接使用对应的名字来做修改,需要借助this指针

#include 
using namespace std;

class Person
{
public:
	Person(int age)
	{
		//1、当形参和成员变量同名时,可用this指针来区分
		//this指针指向被调用的成员函数所属对象
		this->age = age; //不能这样写 age = age
	}
	int age;
};

void test01()
{
	Person p1(10);
	cout << "p1.age = " << p1.age << endl;
}

int main() {
	test01();
	return 0;
}

运行,可以看出当成员变量和形参相同时,通过this指针修改了成员变量的值
C++核心编程:P6->类和对象----C++对象模型和this指针_第5张图片


现在我们想增加一个成员函数,实现将一个对象的年龄加到另一个对象的年龄上。

#include 
using namespace std;

class Person
{
public:
	Person(int age)
	{
		//1、当形参和成员变量同名时,可用this指针来区分
		this->age = age;
	}
	void PersonAddPerson(Person &p)
	{
		this->age += p.age;
	}
	int age;
};

void test01()
{
	Person p1(10);
	cout << "p1.age = " << p1.age << endl;
	Person p2(10);
	p2.PersonAddPerson(p1);
	cout << "p2.age = " << p2.age << endl;
}

int main() {
	test01();
	return 0;
}

运行,可以看出结果正确
C++核心编程:P6->类和对象----C++对象模型和this指针_第6张图片

但是如果现在我想在一行多次调用这个成员函数,发现会报错,该怎样修改呢?
C++核心编程:P6->类和对象----C++对象模型和this指针_第7张图片
解决方案:我们这里的成员函数没有返回值,所以肯定会报错。如果每次执行PersonAddAge都能返回当前对象,那么就可以继续调用PersonAddAge,所以可以作如下修改:

#include 
using namespace std;

class Person
{
public:
	Person(int age)
	{
		//1、当形参和成员变量同名时,可用this指针来区分
		this->age = age;
	}
	//以引用的方式返回,返回对象本身
	Person& PersonAddPerson(Person p)
	{
		this->age += p.age;
		//返回对象本身
		return *this;
	}
	int age;
};

void test01()
{
	Person p1(10);
	cout << "p1.age = " << p1.age << endl;

	Person p2(10);
	p2.PersonAddPerson(p1).PersonAddPerson(p1).PersonAddPerson(p1);
	cout << "p2.age = " << p2.age << endl;
}

int main() {
	test01();
	return 0;
}

运行,可以看到成功多次调用了成员函数
C++核心编程:P6->类和对象----C++对象模型和this指针_第8张图片
注意:如果将PersonAddAge以值返回的方式,则结果会不对。这是因为每次返回的不是本体,而是重新创建了一个新的对象。然后下次就会修改这个新的对象的成员变量的值,最后导致最开始调用的那个对象只修改了一次。

#include 
using namespace std;

class Person
{
public:
	Person(int age)
	{
		//1、当形参和成员变量同名时,可用this指针来区分
		this->age = age;
	}
	//以值的方式返回
	Person PersonAddPerson(Person &p)
	{
		this->age += p.age;
		//返回对象本身
		return *this;
	}
	int age;
};

void test01()
{
	Person p1(10);
	cout << "p1.age = " << p1.age << endl;

	Person p2(10);
	//p2加一次后,返回p2的副本。
	//第二次用p2的副本来加,返回p2副本的副本
    //第三次用p2副本的副本来加,返回p2副本的副本的副本
	//所以最后p2就加了1次
	p2.PersonAddPerson(p1).PersonAddPerson(p1).PersonAddPerson(p1);
	cout << "p2.age = " << p2.age << endl;
}

int main() {
	test01();
	return 0;
}

C++核心编程:P6->类和对象----C++对象模型和this指针_第9张图片


三、空指针访问成员函数

C++中空指针也是可以调用成员函数的,但是也要注意有没有用到this指针。如果用到this指针,需要加以判断保证代码的健壮性

首先,我们确认空指针调用成员函数的情况

#include 
using namespace std;

//空指针访问成员函数
class Person {
public:
	void ShowClassName() {
		cout << "我是Person类!" << endl;
	}
};

void test01()
{
	Person * p = NULL;
	p->ShowClassName(); //空指针,可以调用成员函数
}

int main() {
	test01();
	return 0;
}

运行,可以发现空指针也调用了成员函数
C++核心编程:P6->类和对象----C++对象模型和this指针_第10张图片
但是如果成员函数中用到了this指针,就不可以了

#include 
using namespace std;

//空指针访问成员函数
class Person {
public:
	void ShowPerson() {
		//属性的前面其实都默认加了个this,即等同于this->mAge
		cout << mAge << endl;
	}
public:
	int mAge;
};

void test01()
{
	Person * p = NULL;
	p->ShowPerson();  //但是如果成员函数中用到了this指针,就不可以了
}

int main() {
	test01();
	return 0;
}

运行,可以发现报错,因为传入的指针是为NULL
C++核心编程:P6->类和对象----C++对象模型和this指针_第11张图片
解决方案:因此,我们在成员函数中用到this,就加入判断。如果是空指针,就直接return出来。

//空指针访问成员函数
class Person {
public:

	void ShowClassName() {
		cout << "我是Person类!" << endl;
	}

	void ShowPerson() {
		if (this == NULL) {
			return;
		}
		cout << mAge << endl;
	}

public:
	int mAge;
};

void test01()
{
	Person * p = NULL;
	p->ShowClassName(); //空指针,可以调用成员函数
	p->ShowPerson();  //但是如果成员函数中用到了this指针,就不可以了
}

int main() {

	test01();

	system("pause");

	return 0;
}

运行,可以发现代码不崩
C++核心编程:P6->类和对象----C++对象模型和this指针_第12张图片


四、const修饰成员函数

常函数:

①成员函数后加const后我们称为这个函数为常函数
②常函数内不可以修改成员属性
③成员属性声明时加关键字mutable后,在常函数中依然可以修改


我们先测试一下this指针的用途,可以看到this指针本质上就是个指针常量。

#include 
using namespace std;

class Person {
public:
	//this指针的本质是一个指针常量Person* const this;,指针的指向不可修改
	void ShowPerson(){
		//this = NULL; //不能修改指针的指向 
		this->m_A = 100; //但是this指针指向的对象的数据是可以修改的
	}
	int m_A;
};

int main() {
	Person p;
	p.ShowPerson();
	return 0;
}

运行,可以发现this可以修改指向的值,不能修改指向。
C++核心编程:P6->类和对象----C++对象模型和this指针_第13张图片


我们再来看下常函数和mutable的用处。可以看到常函数无法修改普通的成员变量,但是可以修改mutable修饰的成员变量。

#include 
using namespace std;

class Person {
public:
	//this指针的本质是一个指针常量Person* const this,指针的指向不可修改,指向的值可以修改
	//如果想让指针指向的值也不可以修改,需要声明常函数,在函数参数列表后加const
	//这样this指针就变成了const Person* const this
	void ShowPerson() const {
		//this = NULL; //不能修改指针的指向 Person* const this;
		//this->mA = 100; //现在this指针指向的对象的数据也无法修改了
		this->m_B = 100; //mutable修饰的变量还是可以修改的
	}

public:
	int m_A;  //无法修改
	mutable int m_B; //可修改
};

int main() {
	return 0;
}

常对象:

①声明对象前加const称该对象为常对象,也无法修改成员变量的值
②如果成员变量加了mutable,则就可以修改
③常对象只能调用常函数,因为普通函数可能会修改成员变量的值


我们看一下常对象的相关用法

#include 
using namespace std;

class Person {
public:
	void MyFunc() const {}
	void MyFunc2(){}
	int m_A;
	mutable int m_B; //可修改 可变的
};

//const修饰对象  常对象
void test01() {
	const Person person; //常量对象  
	person.MyFunc();     //常对象只能调用常函数
	person.m_B = 100;    //常对象可以修改mutable修饰的成员变量
	//person.MyFunc2();  //常对象不能调用普通成员函数
	//p.m_A = 100;       //常对象不能修改普通成员变量
}

int main() {
	test01();
	return 0;
}

运行,可以发现常对象只能调用常函数,且不能修改普通成员变量,但是可以修改mutable修饰的成员变量。
C++核心编程:P6->类和对象----C++对象模型和this指针_第14张图片

你可能感兴趣的:(C++核心编程,c++,开发语言,visual,studio,c语言,面试)