当前位置: 首页 > article >正文

Learning C++ No.3【类和对象No.2】

引言:

北京时间:2023/2/4/9:52,起床时间8:55,今天为什么可以起这么早呢?原因就是我没有把我的闹钟给放在枕边,哈哈哈!但是好像导致一个8:20的闹钟闹了半个小时,哈哈哈!上边博客带我们大致了解了一下什么是类和对象,然后由于时间安排问题,没有写完,今天,我们就接着上篇博客,来对类和对象,以及相关知识做一个完整一点的学习。

C++中的类和对象

该说不说,在学习C++中的类和对象的时候,我们第一是要搞清楚什么是类,什么是对象,然而,这两个东西我们其实是了解一点的,只是跟我们以前了解的,有了一些的改变,所以在我们学习类和对象的前提下,我们先去复习一下有关C语言中的结构体的知识,使我们可以更好的掌握C语言的语法和学习接下来的类和对象。

复习C语言中的结构体

下图就是C语言中如何定义一个结构体的代码,从最简单的结构体到C语言中最完整的结构体定义,当然在结构体中我们还可以增加各种的变量,这取决于你需要的数据结构的类型

在这里插入图片描述
并且当我们复习完了如何定义结构体之后,我们复习一下如何使用结构体,如下就是对普通结构体的一个最基本的初始化,和打印结构体中的内容的代码,我们通过看代码的方式来回忆我们以前对结构体的认知,从而达到复习结构体的效果。
如图:

正式开始C++中的类和对象

当我们复习了C++中有关结构体相关的知识之后,我们现在就可以来看一下C++中的类是什么了,其实类的本质上还是一个结构体,只是此时在C++中叫做类,在C语言中叫做结构体,当时结构体不等于类,类是结构体的进化,为什么说是进化呢?因为我们都知道,在C语言中,我们的结构体中只可以定义我们需要的结构体成员变量(如:int、char、int*)等,而在C++中,我们的类中,此时不仅可以定义结构体成员变量,而且此时还可以定义函数,就是将函数的定义,直接写进我的类(结构体)中,所以C语言中结构体和C++中的类最明显的区别就是类中可以定义结构体。
如下图所示:

看到上图,我们就很明显的发现,我们此时在C++中是可以直接在类(结构体)中定义函数的,不用再把函数定义在类的外面了,并且我们发现,在C++中,我们的struct可以写成class(类),**所以我们知道了一个新的关键字,就是class(类)**,并且我们的类中的内容被称为**类的成员**,类中的变量称为**类的属性**或者成员变量,类中的函数称为**类的方法**或者成员函数。

当然除了上图中类中可以定义函数这个特点之外,我们C++中的类和C语言中的结构体还是有很大的区别的,例如:我们以前在实现链表的时候,我们需要一个一个相同类型的结构体构建,此时只能在链表结构体中加一个该结构体的指针,此时的指针是struct ListNode*next类型的(不可以使用typedef之后的,必须要加上struct),但是此时在我们的类中,我们定义了一个类之后,此时struct后跟的就不再是只是类的名字了,此时跟的就是类,所以就可以把struct后面的名字当作类来使用,不需要再像C语言中的结构体一样加上struct了。结合下图,就很好理解了。

如图:

了解了上述知识,我们大致知道了什么是C++中的类了,接下来就让我们更近一步,看一下类在不同的文件中是如何定义使用的,如下图,我们看一下类在test.cpp和test.h中的类的成员函数声明和定义分离是怎样的。关键就是注意:搞清楚归属,该函数是全局函数,还是类中的函数。关键点:(::)
在这里插入图片描述

类的访问限定符及封装

类的访问限定符是什么,相信大家都有这个疑问,接下来让我们带着这个疑问,我们来看一看什么是访问限定符吧!如下图代码,我们会发现,当我们使用了class(类)来构建我的代码时,是没有问题的,但是当我想要调用这个类中的函数的时候,编译器却报错了,这是为什么呢?此时答案就涉及到了我们的访问限定符的概念了。

C++中的访问限定符

|public修饰的成员在类外可以直接被访问 |- |**protected和private修饰的成员在类外不能直接被访问** |**访问权限所用域从该访问限定符出现的位置开始直到下一个访问限定符的出现为止** |**如果后面没有访问限定符,作用域到}的位置结束** |**class的默认访问权限为private,struct为public(因为struct要兼容C语言)**

所以通过上述的知识,我们知道了class的默认访问权限为private,所以我们可以知道,我们上述代码出问题的原因就是我们使用了class,但是我们没有使用public权限,我们不能从class中拿到我们的函数,因为class的默认访问权限为private(私有的)。

改进代码如下:

此时加了public就和我们想象中的一样了,我们可以在class的外部使用class内部的函数了。并且一般我们的类属性是类私有的,类作用有的才是公有的,所以类中的东西,私有公有都是由我们自己决定的哦!

严谨编写代码

我们此时了解了上述知识,会发现一个问题,我的参数和我的类属性(成员变量),有时会出现一定的问题,就是类属性容易和我的参数的名字相同,导致我们对参数和类属性有歧义,所以此时我们在编写代码的时候,通常我们会给类属性的前面加上一个(_)来表示该变量是一个成员变量,而不是参数。如下代码:

类的封装

当我们知道并且会使用我们的访问限定符的使用,我们接下来来理解理解我们的类的封装,此时我们了解了封装,我们就顺便谈谈面向对象的三大特性:封装、继承、多态,所以我们就带着这三大特性去学习接下来的C++中的类和对象吧!首先我们这里就来讲讲我们的封装,什么是封装呢?**封装:将数据和操作数据的方法进行有机结合,隐藏对象的属性和实现细节,仅对外公开接口和对象进行交互。**并且封装的本质就是一种对类的管理,使用户可以更好的使用类。

类的作用域

其实上述我们有讲到一些什么是类的作用域,此时我们在展开来讲一下,当我们定义一个类(结构体)时,我们定义的类就是一个新的作用域,类的所有成员都在类的作用域中,在类的外部定义类成员时,此时就不能直接给你使用,因为类是有自己的作用域的,所以此时就要使用一个操作符(::),这样就可以指明该成员属于的是那个类的作用域。

代码如下:

类的实例化

搞定了上述的知识,此时我们就来了解一下什么叫做类的实例化,但是在我们理解什么是实例化之前,我们先看一下下面代码,我们再来深入了解一下类中的成员变量。

如下图:

此时可以看出,我们上述代码是一个类,在这个类中有三个成员变量,很多同学会认为这个是属于变量的声明,因为平时我们在函数中,无论是普通函数,还是main函数,我们在该函数内部定义一个变量都是真正的定义,因为在这些函数中,只要一定义函数,就有相应的栈帧空间来存储该函数定义的变量,所以在函数中,我们使用各种类型定义的变量都是属于变量的定义,不是变量的声明,然而此时我们并不是在函数之中,而是在class(类)中,因为是在类中,所以此时并没有想函数一样的栈帧空间或者动态空间甚至静态空间来存放我们声明的变量,所以此时的变量就不属于变量的定义,而是属于变量的声明,所以如图中所示,我们的成员变量只是声明,并不是定义。

总:在类中的变量,由于没有空间存储的原因,所以只是声明,并不是定义。

此时我们明白了上述的这个小知识,我们就可以来正式了解一下什么类的实例化了,类的实例化本质就是使用我们的类去创建一个对象(变量),此时使得这个类得到栈帧空间,此时类就被存储到了我们的内存之中,此时就实现了我们的类的实例化,所以这也就是我们类实例化的概念。

如图:就是一个类的实例化的代码

这样,我们就可以通过类的实例化使类获得空间,从而让我们可以使用类。明白了这个道理之后,有的同学可能就很好奇,类中不是还有函数吗,那么此时的函数到底是声明还是定义呢?其实这个问题是非常的简单的,从上述的内容,我们就可以知道,我们的函数是可以直接从栈上获取栈帧空间的,所以类中的函数是属于定义而不是声明,形象点,我们通过计算该类的所占内存大小来看到答案,可以猜测如果类所占的内存大小为12,那么该类中确实是只包含了成员变量空间,如果不是那么该类中就有包含函数空间,那么函数就是属于声明而不是定义,所以如下图:

可以看出此时和我们的猜想是一样的,所以得出结论:类中的函数因为没有占用对象的空间,而是自己形成栈帧,是定义,而不是声明。 并且此时我们可以联想到一个问题,就是为什么我的成员变量是存在对象空间中,而我的成员函数不是存在我的对象空间中呢?原因:可以节省我们对象所占内存的大小,并且把定义的函数放在一个叫代码段的地方,使其实现公共(代码段就是一个公共区域),这样,我们每次使用同一个函数的时候,就不要重新在对象空间中开辟空间了,而是可以直接从代码段中获取同一份空间的函数,这样就使我们的内存空间得到很好的使用,并且提高程序效率。

总结:每个对象(d1(变量))调用成员变量是不一样的,调用一次成员变量(公开情况下),就需要在对象上开辟一块空间(大小由类型决定),实现变量数据得到独立存储。但是每个对象调用成员函数是一样的,因为我们的成员函数是放在公共区域(代码段)。

计算类所占内存大小

知道上述知识,我们来尝试计算一下类空间应该要所占的内存大小,如下图也:

此时根据上述知识,成员函数是放在代码段中,只有成员变量是放在类对象中,那么此时答案是多少呢?4 、0、 0 吗?正确答案是4、1、1 ,原因是:只要我们的类被实例化之后,就要开空间,至于空间开多大是由类中的成员变量决定,所以就算我们的类是个空类,此时只要其被实例化,那么该类就会被存储在栈帧上,该类的地址才会被存储,所以空类所占的空间是1 ,可以理解成这个1,就是用来标示我们的类,证明该类存在过。

我们顺便复习一下C语言中的内存对齐的概念:
第一个成员在与结构体偏移量为0的地址处
其它成员变量要对齐到某个数字(对齐数)的整数倍的地址处,VS中对齐数默认是8
结构体总大小为:最大对齐数(所有变量类型最大者与默认对齐数参数取最小)的整数倍
如果嵌套了结构体的情况,嵌套的结构体对齐到自己的最大对齐数的整数倍出,结构体的整体大小就是所有最大对齐数(含嵌套结构的对齐数)的整数倍

什么是this指针

想要学习什么是this指针,此时我们就要先了解一个概念,这一系列的概念都是和我们C++中类这个东西有关的,一切都是从类的使用延伸出来的问题,例:在类中,我们通过上述知识得知,类中的函数在类被实例化之后,函数并不存储在类对象中,而是存储在特定的代码段中,存储在代码段中,起一个共享的作用,那么此时经过这个现象,我们可以发现一个问题,就是当我使用该类创建了两个类对象出来时,并且此时用这两个类对象同时调用了类中的某一个函数,那么此时该函数是属于那一个类对象呢?该函数存储在那一个类对象的空间中呢?所以我们从这个问题出发,我们就可以很好的去认识一下什么是this指针了。

如下图:

此时出现了问题,我们就要来解决这个问题,如何解决呢?其实我们的编译器已经是帮我们解决了编译器在识别出我们调用了代码段中的共享函数的时候,编译器自己会生成一个this指针,通过这个this指针来标示我们的调用对象,从而确定该函数是被那个调用对象给调用。并且本质上我们的this指针就是成员函数的一个形参。所以this指针是存在该函数的栈帧上的(不可以认为是存在对象中的,因为内存大小并没有算this指针)

如下图:
在这里插入图片描述
所以这样我们的编译器就可以很好的通过this指针来区分谁是谁调用的函数了。

并且此时我们通过上面的这幅图,我们可以看出,,我们打印了我们的this指针,发现我们的this指针确实是一个地址,强调,我们的this指针确实是一个地址,并且发现,我们调用了两次Init函数,确实是执行了两次不一样的函数调用,然后生成两个不一样的地址,并且这两个不一样的地址,就是我们的d1和d2的地址,也就是我们调用对象的地址,所以现在可以充分证明,我们的this指针可以帮我们区分出共享函数到底是属于那个对象。

此时我们通过理解图中的两句代码,来深入理解一下this指针:

此时想解决第一条代码,这个一定要回想起我们this指针的知识,因为this指针的作用就是让我们知道该函数是被那个对象所调用(这里this的作用就是让我们知道,我们的函数是被ptr这个对象所调用),但是又由于ptr->Function();ptr指针已经指向了我的Function了,我已经知道我的Function是被ptr这个指针所调用,那么此时this指针就已经相当于是ptr指针,两个指针起着一样的作用,所以该句代码是没有问题的,目的只是为了搞定楚Function是被ptr指针调用。但是我们看第二句代码可以发现,它和第一句代码是一样的,同时使用了ptr指针作为this指针来表明此时它是使用ptr指针来调用我们类中的共享函数Init,这样做本来是合理的,因为ptr指针就是this指针,但是我们又发现在调用Init函数的时候,我们把参数也传递给了我们的调用函数,此时就会导致这些参数会去使用我们的this指针去访问我们的成员变量,然后把参数数据给赋值给我们的成员变量,但由于此时该this指针已经变成了ptr指针,变成了一个空指针,我们拿着这个空指针去访问我们的成员变量,等于是用空指针去对成员变量进行解引用,此时自然而然是不可以的,所以第二句代码本质是一个错误的代码,原因就是:使用了空指针去访问成员变量。

总:this指针的使用在C++中的类的使用中是非常的重要的。

总结:我们今天把类的表面初步学习了一下,下篇博客我们就来挖一挖类里面的东西和运算符重载有关的知识了。

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

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

相关文章:

Learning C++ No.3【类和对象No.2】

引言: 北京时间:2023/2/4/9:52,起床时间8:55,今天为什么可以起这么早呢?原因就是我没有把我的闹钟给放在枕边,哈哈哈!但是好像导致一个8:20的闹钟闹了半个小时,哈哈哈!上…...

c++ 控制台应用程序静态库使用实例

1、新建工程: 之后点击【完成】即可 2、新建类TestHelper (1)头文件 #pragma once class TestHelper { public: TestHelper(); ~TestHelper(); int AddSum(); }; (2)cpp文件 #include "stdafx.h&qu…...

分享116个图片切换JS特效,总有一款适合您

分享116个图片切换JS特效,总有一款适合您 116个图片切换JS特效下载链接:https://pan.baidu.com/s/1aPAL2Ag_7U8kLcIqHIyxnw?pwdyqxe 提取码:yqxe Python采集代码下载链接:https://wwgn.lanzoul.com/iKGwb0kye3wj 咪咕音乐官…...

测试贵在坚持,看一名普通测试人员的经历

时光飞逝,物是人非,蓦然间,2022年已成为历史。 前言… 说实话,本来是不想写这篇总结的,因为我的2022年过得实在是糟心,从四月开始到年底,我的经历都是磕磕绊绊,总结起来无非就是…...

Grafana 系列文章(七):Grafana Explore 中的 Tracing

👉️URL: https://grafana.com/docs/grafana/latest/explore/trace-integration/ 📝Description: Tracing in Explore Explore 允许你将 tracing 数据源的痕迹可视化。这在 Grafana v7.0中可用。 支持的数据源有。 JaegerTempoX-RayZipkin 关于如何为…...

Windows常用小技巧集锦(持续更新ing...)

诸神缄默不语-个人CSDN博文目录 本文介绍一些在Windows系统的学习与使用过程中,会碰到的小问题和解决方案。 最近更新时间:2023.2.5 最早更新时间:2023.2.5 windows系统下使用cd命令_龙虾小兵的博客-CSDN博客_cd windows解决win10快速访问…...

JavaSE笔记总结02(Collection集合List、Set、Map)

7 常用API 7.1 Object类 toString方法 概念 常用方法:1.public String toString():--默认是返回当前对象在堆内存的地址信息-- 默认是地址信息格式:类的全限名内存地址-- 直接输出对象名称,默认会自动调用toString--开发中直接输出对象&am…...

Connext DDS开发指南(2)—发布/订阅

Connext DDS支持的最基本的通信模式是发布/订阅publish/subscribe模式。 发布/订阅是一种通信模式,数据生产者“发布”数据,数据消费者“订阅”数据。这些发布者和订阅者不需要提前了解彼此;它们在运行时动态地dynamically发现彼此。他们共享的数据用“主题Topic”来描述,发…...

【Maki ‘ s Lab学习讲座】超前学习法

作者:MakiMaki的完美算术教室 排版: PenguinIT鹅 当人们愉快地承受苦难时,苦难也会变得美丽,这不是麻木,而是由于心灵的伟大。 ——亚里士多德 Maki’s Lab简介: Maki’s Lab核心成员来自多伦多大学,清华大学等世界各地名校。M…...

spring 生命周期

你知道spring是管理的Bean 何时被创建,何时初始化完成,以及何时被销毁的吗?我们一起讨论讨论。先来看看spring的作用域有哪些?1、singleton 是默认的作用域,也就是说,当定义 Bean 时,如果没有指…...

栅格化原理和代码

栅格化原理 把某个点根据经纬度放在整数经纬度记录的格子里,并把格子编号与点对应起来。 第一步确定每个格子的长和宽,即经度变化量和纬度变换量: 假设测试点的经纬度是(114度, 22.5度) 划定栅格划分的经纬度范围(大范围&…...

哈希介绍以及综合运用

文章目录1.unordered 系列容器的介绍1.1 unordered_map介绍1.2 其和普通map的区别1.3unordered_map 底层2.hash基础原理2.1基础原理2.2哈希函数3.哈希冲突解决3.1闭散列**也叫开放定址法,当发生哈希冲突时,如果哈希表未被装满,说明在哈希表中…...

使用 Arrays.asList/ArrayList(subList)注意事项

1. 使用Arrays.asList的注意事项 1.1 可能会踩的坑 先来看下Arrays.asList的使用&#xff1a; List<Integer> statusList Arrays.asList(1, 2); System.out.println(statusList); System.out.println(statusList.contains(1)); System.out.println(statusList.con…...

啥牌子的蓝牙耳机经济好用?无线耳机便宜好用的品牌推荐

随着蓝牙耳机市场越来越大&#xff0c;蓝牙耳机品类越来越多&#xff0c;选择增多的同时不免产生怎么选的烦恼。最近看到很多人在问&#xff0c;啥牌子的蓝牙耳机经济好用&#xff1f;下面&#xff0c;我来给大家推荐几款便宜好用的无线耳机&#xff0c;可以当个参考。 一、南卡…...

day 2-正则

day 2-正则 os模块&#xff1a; os.mkdir(文件夹路径) - 在指定的位置创建指定文件夹 # os.mkdir(files/test)# os.path.exists(文件夹路径/文件路径) - 判断指定文件夹或者文件是否存在 if not os.path.exists(files/test):os.mkdir(files/test)一、正则的作用 可以让复杂…...

2023国际嵌入式展精彩回顾 | Imagination宣布最新汽车合作进展、RISC-V是大热门

不久前&#xff0c;2023年国际嵌入式展(embedded world)在德国纽伦堡会展中心举行。展会期间&#xff0c;Imagination不仅分享了RISC-V的话题&#xff0c;还宣布了与Telechips 、 CoreAVI和Ashling等合作伙伴最新的合作进展。汽车解决方案一直是纽伦堡过去活动的主题&#xff0…...

企业短视频推广怎么玩?制造业短视频推广干货分享

短视频作为一种新型营销方式&#xff0c;已经成为企业推广的重要手段。通过合理的推广策略、精美的短视频制作、适当的社交媒体平台选择和与用户的互动&#xff0c;企业可以实现短视频推广的效果。同时&#xff0c;借助短视频制作工具可以提高制作效率和降低制作成本&#xff0…...

高防IP与高防CDN的区别

随着互联网的流量增加&#xff0c;很多行业都开始选择高防御产品&#xff0c;当业务受到攻击时&#xff0c;我们通常选择高度防御的服务器。然而&#xff0c;即便如此&#xff0c;在使用过程中可能会有更多的攻击&#xff0c;使服务器无法防御。为了避免通过传输数据来改变服务…...

从 Python 中的字符串中删除最后一个分号或者逗号

第一种方法 使用 str.rstrip() 方法从字符串中删除最后一个逗号&#xff0c;例如 new_str my_str.rstrip(;)。 str.rstrip() 方法将返回删除尾随逗号的字符串副本 str 颜色:高帮下单备注;尺寸:42; new_str str.rstrip(;) 运行结果&#xff1a; 第二种方法 str 颜色:高帮下单…...

Vmware中桥接无法获取IP

Vmware设置虚拟操作系统网卡为桥接模式后&#xff0c;本应该和本地网卡获取到同一网段的IP的&#xff0c;但现在突然无法获取到IP设置&#xff0c;原因是什么呢&#xff1f;经过查看&#xff0c;发现Vmware中的网络编辑器中的桥接网卡桥接到了一个虚拟网卡上&#xff0c;更改到…...