【 C++ 】 类和对象的学习

news2024/11/17 16:16:53

前言:

😘我的主页:OMGmyhair-CSDN博客

目录

引言:

一、类的作用域

二、计算类对象的大小

三、this指针

this指针❓1

this指针❓2

this指针❓3


引言:

通过类我们可以对数据和方法进行封装

封装的意义:

1.将属性和行为作为一个整体,表现事物

2.类在设计时,可以把属性和行为放在不同的权限下,加以控制

在c++中,类与结构体差不多,它们唯一的区别是,结构体的默认权限是公有public,而类的默认权限是私有private。从下图可以看到,我们不能对class的成员变量进行访问。


一、类的作用域

当我们定义了一个类时,这个类也定义了一个新的作用域,类的所有成员都在这个作用域中。当我们在类外定义成员时,我们要用上::作用域操作符告诉编译器这个函数属于哪个作用域,否则编译器在编译时,不会到类域中去查找而是在全局或者局部查找,此时可能会产生错误。

我们举个例子帮助理解:

class Person
{
public:
	void SetAge(int age)
	{
		_age = age;
	}
	void PrintAge()
	{
		cout << _age << endl;
	}
private:
	int _age;
};

int main()
{
	Person lisi;
	lisi.SetAge(18);
	lisi.PrintAge();
	return 0;
}

上面代码中,当我们想要将PrintAge函数定义在类外时,必须用::作用域操作符指定这个函数的作用域。要不然,编译器会将这个函数当作全局函数,在编译到_age时,不会到类域中去查找,因此编译器上下前后都找不到_age,此时就发生了问题。

正确做法:

报错做法:

❗值得一提的是,C++ 标准规定所有在类定义中定义的成员函数都是内联的。但是当类中的成员函数定义和声明分离是就不是内联了。

❗这里要注意一点,类中的成员变量是声明而不是定义,定义需要给变量开空间,而声明不需要给变量开空间。而这一点会在后面的对象实例化更详细的讲解。

例如像下面的写法是错误的,因为_age只有声明,没有定义,没有为其开空间,我们无法使用。可以看到,即使我将_age变为公有的,我们仍然无法进行访问。


二、计算类对象的大小

当我们用类实例化出一个对象时,此时就会为成员变量开辟空间,我们得以进行访问。

你是否会好奇这一个对象实例化后,它的大小是多少呢?

你可能会认为这个对象的内存是由成员变量和成员函数组成,但是我们仔细想想,让这些对象不同的东西就是成员变量,而那些成员函数不管对象怎么变,它们的代码都是一样的,所以我们不需要浪费空间每次示例化一个对象都为这个对象存储一套代码。

这时候我们知道了对象的空间由成员变量组成,那怎么去计算呢?这里对象也符合内存对齐规则,类的对象内存计算方式与结构体相同。

点击关于结构体知识点的总结_结构体变量赋值知识点总结-CSDN博客你可以得到详细的内存对齐规则内容以及如何计算。

现在我们已经了解了对象的大小如何计算,也知道了对象的内存放的是成员变量,那么以下这种情况如何计算呢?

class Person
{
public:
	void Print()
	{
		cout << "age" << endl;
	}
};

int main()
{
	Person lisi;
	return 0;
}

现在没有成员变量,那么lisi这个对象的大小为0吗?那岂不是这个对象不存在?我们用sizeof计算一下:

为了表示这个对象存在,会给这个对象开辟一个字节空间进行占位。

❗所以,没有成员变量的类实例化出来的对象大小为1字节,这1字节不存储任何东西,单纯用于占位。


三、this指针

this指针指向被调用的成员函数所属的对象

this指针是隐含每一个非静态成员函数内的指针

用途:

1.当形参和成员变量同名时,可用this指针来区分

2.在类的非静态成员函数中返回对象本身,可使用return *this;

首先,上一份代码:

class Person
{
public:
	void Print()
	{
		cout << _age << endl;
	}

	int _age;
};

int main()
{
	Person lisi;
	Person wfive;
	lisi._age = 20;
	wfive._age = 25;
	lisi.Print();
	wfive.Print();
	return 0;
}

你是否会好奇,Print函数是怎么知道谁在调用它呢?当lisi调用它时,它是怎么知道自己该输出lisi的年龄而不是wfive的呢?

这里我们就要引入一个东西,它就是this指针。

其实编译器编译后,成员函数会默认在形参的第一个位置添加该类类型的指针,其实真实的Print函数长这样:

真实的:

class Person
{
public:
	void Print(Person * const this)
	{
		cout << this->_age << endl;
	}

	int _age;
};

int main()
{
	Person lisi;
	Person wfive;
	lisi._age = 20;
	wfive._age = 25;
	lisi.Print(&lisi);
	wfive.Print(&wfive);
	return 0;
}

每次对象在调用成员函数时,都会偷偷将对象自己的地址传过去,这一成员函数就能知道是谁在调用它,当lisi调用时Print函数也得以知道自己该打印lisi的年龄。

虽然真实的成员函数和调用是上面那样,但是你不需要在实参和形参显式地写出this指针,那样编译器会报错。但是我们可以在函数体中显式地使用this指针。

并且this指针本身不能被修改,因为const修饰的是this,即this不能再指向别的地址。

如果是Person const * this,那么*this不能改变,但是this能指向别的地址。

现在用几个问题加深我们对this指针的理解:

this指针❓1

this指针存在内存哪个区域的 ()

A. 栈 B.堆 C.静态区 D.常量区 E.对象⾥⾯

解答:

首先我们知道对象里面只存储成员变量,因此排除E。

这个问题答案是A,this指针是形参,因此放在栈帧里面,但是这个答案也不完全正确。在VS中,this指针存放在寄存器ecx中,因为this指针要频繁使用,所以放在寄存器中更快。

this指针❓2

下⾯程序编译运⾏结果是() A、编译报错 B、运⾏崩溃 C、正常运⾏

class A
{
public:
	void Print()
	{
		cout << "A::Print()" << endl;
	}
private:
	int _a;
};

int main()
{
	A* p = nullptr;
	p->Print();
	return 0;
}

解答:

首先,Print函数会传对象的地址,而这里对象的地址就是空指针。我们结合下面的代码帮助理解:

A a;
A* pa = &a;//pa==&a  *pa==a

A*const this=&a;//A*const this =pa;

因此p就是nullptr,而p就是对象的地址,nullptr被传给了this指针,this就是空指针。我们可以将Print中的this打印出来看看:

在Print函数中,我们并没有使用this指针,不存在解引用空指针的问题,所以它可以正常运行。

正确答案为C

this指针❓3

下⾯程序编译运⾏结果是() A、编译报错 B、运⾏崩溃 C、正常运⾏

class A
{
public:
	void Print()
	{
		cout << "A::Print()" << endl;
        cout << _a << endl;
	}
private:
	int _a;
};

int main()
{
	A* p = nullptr;
	p->Print();
	return 0;
}

解答:

答案选B

因为在Print中,我们进行了对空指针的解引用,从上一题我们知道了这种情况下this为空指针,而cout<<_a<<endl这一行代码实际上是cout<<this->_a<<endl,在这一过程中发生了对this指针也就是空指针的解引用。因此程序运行崩溃。



如果这篇文章有帮助到你,请留下您珍贵的点赞、收藏+评论,这对于我将是莫大的鼓励!学海无涯,共勉!😘😊😗💕💕😗😊😘



本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1955938.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【Android】实现一个优雅的自定义底部导航栏(速通安卓大作业必备)

文章目录 前言一、实现思路二、代码实现流程①修改theme&#xff1a;②在color文件中添加颜色&#xff1a;③添加图标文件④添加选中时布局的背景⑤修改布局文件⑥按钮效果图&#xff1a;⑦修改MainActivity中的代码⑦创建各个界面的Fragment⑧运行结果&#xff1a; 三、 总结 …

【学术会议征稿】第五届人工智能与教育国际学术会议(ICAIE 2024)

第五届人工智能与教育国际学术会议&#xff08;ICAIE 2024&#xff09; 2024 5th International Conference on Artificial Intelligence and Education 第五届人工智能与教育国际学术会议&#xff08;ICAIE 2024&#xff09;由集美大学诚毅学院主办&#xff0c;闽南师范大学…

福昕PDF编辑器v13专业版 授权版

福昕高级PDF编辑器是一款功能强大的PDF文件编辑软件&#xff0c;提供多种实用的编辑功能。 软件截图&#xff1a; 使用说明&#xff1a; 解压后&#xff0c;双击start.bat来运行软件 下载地址&#xff1a;FoxitPDFEditor-Pro-v13 解压密码&#xff1a;helloh 下载时可能会有…

动手学大模型应用开发笔记--用dash开发一个大模型知识库

简介 动手学&#xff0c;把自己学到的东西动手自己做出来并输出&#xff0c;是最好的学习方式。最近一直在关注和使用各种ai工具&#xff0c;也在学一些ai开发的知识&#xff0c;看到datawhale的开源学习教程&#xff0c;动手学大模型开发( [github.com/datawhalech…])这个教…

高频面试题基本总结回顾(含笔试高频算法整理)暂存篇

干货分享&#xff0c;感谢您的阅读&#xff01; &#xff08;暂存篇---后续会删除&#xff0c;完整版和持续更新见高频面试题基本总结回顾&#xff08;含笔试高频算法整理&#xff09;&#xff09; 备注&#xff1a;引用请标注出处&#xff0c;同时存在的问题请在相关博客留言…

dockerfile部署镜像 ->push仓库 ->虚拟机安装建木 ->自动部署化 (详细步骤)

目录 创建私服仓库 vi /etc/docker/daemon.json vim deploy.sh判断脚本内容 创建 建木 后端部署 命名空间 设置密码用户名 创建git仓库 gitignore文件内容 图形项目操作 git maven docker镜像 点击流程日志 vim /etc/docker/daemon.json 执行部署脚本 ip 开发…

代码性能优化(3)——聊聊多线程

代码的性能优化&#xff0c;有些是从逻辑层面进行的&#xff0c;比如同时对50W个人发放奖励&#xff0c;可以改成用户登录的时候&#xff0c;自动领取有没奖励&#xff0c;或者统计每日的每个业务员的销售额和实时累积的销售额&#xff0c;将实时sum函数改成&#xff0c;每一笔…

24种设计模式介绍与6大设计原则(电子版教程)

前言 您是一个初级的 coder,可以从中领会到怎么设计一段优秀的代码&#xff1b;您是一个高级程序员&#xff0c;可以从中全面了解到设计模式以及 Java 的边角技术的使用&#xff1b;您是一个顶级的系统分析师&#xff0c;可以从中获得共鸣&#xff0c;寻找到项目公共问题的解决…

StarRock3.3 安装部署

服务器前置要求&#xff1a; 1、内存>32GB 2、JDK 8 is not supported, please use JDK 11 or 17 1、安装 wget https://releases.starrocks.io/starrocks/StarRocks-3.3.0.tar.gz tar zxvf StarRocks-3.3.0.tar.gz 2、FE服务启动 2.1 配置FE节点(默认配置&#xff0c;…

dns和 openELB

DNS yum -y install bind允许其他的主机来监听&#xff0c;允许其他的主机来查询&#xff0c;改这两个地方就行了。 把需要解析的文件都添加进来&#xff0c;cp -p的意思是保留原来的权限控制 注意本地dns放在 DNS1 二、负载均衡 OpenELB Layer2 模式 BGP模式 OpenELB …

DBeaver使用SQL脚本编辑器

文章目录 1 新建脚本2 选择数据库3 编写脚本【按行执行】参考 1 新建脚本 2 选择数据库 3 编写脚本【按行执行】 光标放到需要执行的行上&#xff0c;点击【最上面的按钮】 或者选中某片代码&#xff0c;然后执行 也可以编写一个脚本然后执行 参考 dbeaver安装和使用教程 …

Linux文件恢复

很麻烦 一般还是小心最好 特别恢复的时候 可能不能选择某个文件夹去扫描恢复 所以 删除的时候 用rm -i代替rm 一定小心 以及 探索下linux的垃圾箱机制 注意 一定要恢复到不同文件夹 省的出问题 法1 系统自带工具 debugfs 但是好像不能重启&#xff1f; testdisk 1、安装 …

酒店智能门锁接口pro[0922]D801 对接收银-SAAS本地化-未来之窗行业应用跨平台架构

proUSB接口函数[0922中性版]-D801 调用函数库&#xff1a; 提供Windows下的32位动态连接库proRFL.DLL&#xff0c;函数使用详细说明 //-----------------------------------------------------------------------------------// 功能&#xff1a;读DLL版本&#xff0c;不涉…

【大数据】:hdfs相关进程启停管理命令

HADOOP_HOME/sbin/start-dfs.sh&#xff0c;一键启动HDFS集群 执行原理&#xff1a; 在执行此脚本的机器上&#xff0c;启动SecondaryNameNode 读取core-site.xml内容&#xff08;fs.defaultFS项&#xff09;&#xff0c;确认NameNode所在机器&#xff0c;启动NameNode 读取wor…

AI变现:科技与商业化的交织

随着科技的飞速发展&#xff0c;人工智能&#xff08;AI&#xff09;已经从科幻电影中的概念走进了现实生活的各个领域&#xff0c;深刻影响着经济、社会与科技的发展。AI不仅代表着技术的革新&#xff0c;更是推动商业变现的重要力量。本文将深入剖析AI的发展历程&#xff0c;…

数据结构(5.3_5)——二叉树的线索化

第一种寻找中序前驱方法 中序线索化 本质上就是一次中序遍历&#xff0c;只不过需要在一边遍历一边处理结点线索化 代码&#xff1a; //全局变量pre 指向当前访问结点的前驱 ThreadNode* pre NULL;struct ElemType {int value; };//线索二叉树结点 typedef struct ThreadNode…

姜夔,师法自然的不仕道人

姜夔&#xff08;ku&#xff09;&#xff0c;字尧章&#xff0c;号白石道人&#xff0c;约生于南宋绍兴二十四年&#xff08;公元1154年&#xff09;&#xff0c;卒于南宋嘉定十四年&#xff08;公元1221年&#xff09;&#xff0c;享年67岁。他的艺术成就涵盖了诗词、散文、书…

JAVA (IO流) day7.25

ok了家人们今天继续学习io流&#xff0c;废话不多说&#xff0c;我们一起看看吧 一.File类 1.1 File类的概述 通过 File 类与 IO 流进行搭配使用就可以通过 Java 代码将数 据从内存写入到硬盘文件 或者从硬盘文件读取到内存 中。 File 类就是文件和目录路径名的抽象表示形式…

ElasticSearch(es)倒排索引

目录 一、ElasticSearch 二、倒排索引 1. 正向索引 2. 倒排索引 具体细节 1. 文档分析 2. 索引构建 3. 索引存储 4. 词条编码 5. 索引优化 6. 查询处理 示例 总结 3. 正向和倒排 三、总结 倒排索引的基本概念 为什么倒排索引快 一、ElasticSearch Elasticsear…

让开发者生活更轻松的 JavaScript 字符串方法

前端岗位内推来了 JavaScript 最初被引入作为一种简单的客户端脚本语言&#xff0c;但现在&#xff0c;它已经成为一种真正的 WORA&#xff08;一次编写到处运行&#xff09;语言&#xff0c;使开发者能够构建桌面、移动、电视、CLI 和嵌入式应用程序。JavaScript 的初学者友好…