IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> C++知识库 -> C++核心编程:P6->类和对象----C++对象模型和this指针 -> 正文阅读

[C++知识库]C++核心编程:P6->类和对象----C++对象模型和this指针

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


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

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


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

#include <iostream>
using namespace std;
#include <string>

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 <iostream>
using namespace std;
#include <string>

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 <iostream>
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 <iostream>
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 <iostream>
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 <iostream>
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 <iostream>
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 <iostream>
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 <iostream>
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 <iostream>
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 <iostream>
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++知识库 最新文章
【C++】友元、嵌套类、异常、RTTI、类型转换
通讯录的思路与实现(C语言)
C++PrimerPlus 第七章 函数-C++的编程模块(
Problem C: 算法9-9~9-12:平衡二叉树的基本
MSVC C++ UTF-8编程
C++进阶 多态原理
简单string类c++实现
我的年度总结
【C语言】以深厚地基筑伟岸高楼-基础篇(六
c语言常见错误合集
上一篇文章      下一篇文章      查看所有文章
加:2022-05-21 18:44:48  更:2022-05-21 18:46:55 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/23 19:16:32-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码