栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > C/C++/C#

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

C/C++/C# 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

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 字节。这是因为编译器为了区分空对象占内存的位置,给每个空对象也分配一个独一无二的内存地址。


当类中有成员变量时,看可能会占多大空间。我们这里在类中添加一个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字节。

若包含了静态成员变量,则它不属于类对象,类型对象的大小不变

当我们添加静态和非静态成员函数时,类对象大小也不变,因为它们也不属于类对象上。


二、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指针修改了成员变量的值


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

#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;
}

运行,可以看出结果正确

但是如果现在我想在一行多次调用这个成员函数,发现会报错,该怎样修改呢?

解决方案:我们这里的成员函数没有返回值,所以肯定会报错。如果每次执行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;
}

运行,可以看到成功多次调用了成员函数

注意:如果将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++中空指针也是可以调用成员函数的,但是也要注意有没有用到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;
}

运行,可以发现空指针也调用了成员函数

但是如果成员函数中用到了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

解决方案:因此,我们在成员函数中用到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;
}

运行,可以发现代码不崩


四、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可以修改指向的值,不能修改指向。


我们再来看下常函数和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修饰的成员变量。

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/892636.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号