大家好啊,好久没有更新了。因为本人的愚笨,想与大家分享的话肯定还得自己明白了才能给大家分享吧。所以这几天都在内部消化。好给大家优质的文章。当然我写的肯定还是很有问题的,希望大家可以在评论区里面指出来。好,废话不多说,我们买上来讲今天的主题。
类定义
当然,如果前期没有预习过的话,看到这个主题可能会思考一下。类是什么东西。有什么用,怎么表示。这是我们没有预习过的疑问。我们来一一解答。
类:是对某一类具有共同特征的事物的抽象描述。例如电脑类,具有键盘、屏幕、电源键等共同特征。但我感觉这样说也有点模糊。但大家可以向这方面想。并且类是一种用户自定义的引用数据类型,它的本质是数据类型,并不是数据,所以不存在于内存中,不能被直接操作,只有被实例化为对象时,才会变得可操作。但是如何实例化,我们后面是会讲的。
好了,讲了这一些后。我们还是来讲讲实际的。类如何创建以及类成员是否有要求。对于类的创建的话,与我们以前的结构体是有相似之处的。大家先看一下下面的图片:
首先我们只看我们看的懂的。我们在c++的第一篇博客中写过,最简答的打印代码吧。所以我们能看懂那个在空中台上打印实验的代码吧。然后就是在private下面的几个定义 。只是我们没有赋值嘛。只是大多数情况也只是定义一下。好了。除了上面这两个就是我们的主函数了。我们可以看到我们的第一个是不是像一个取别名呀。就是我们的上一篇博客中的那个别名。大家可以打个模糊眼觉得这样差不多。也可以想象成boss与下属的关系。毕竟不能每一件事情都要boss亲自出马吧。需要boss出马的时候肯定占少数。然后就是我们像以前c语言中的指针引用了。只是我们c语言还是用->,但是在c++里,只能这样使用。好了这些就是我们用以前的知识就能显而易见得知的知识了。
接下来就是与我们以前c语言有点不一样的知识点了。我们知道c语言我们用的是struct来创建的。但是我们这里用的是class。但是其实这两个我们在c++里都可以使用,并且都是用来创建类的。那么都是创建类的话,肯定有区别,不然的话,为什么需要两个嘛。对于这两个的区别就是我们图片中的两个单词public和private。这两个单词和protected在c++中被称为访问限定符。这个知识点我们接下来是会讲到的。所以我们这里就不多讲。
我们看到我们这里用class创建类与我们以前创建结构题的区别其实是不大的。都是关键字与名词然后加{}。并且在结尾的}后加上;。这些都是与我们以前的东西差不多的。然后就是我们类体中内容称为类的成员:类中的变量称为类的属性或成员变量; 类中的函数称为类的⽅法或者成员函数。并且大家可以在我上面的图片看出,成员函数与成员变量还是很容易分析出来的吧。
而且我们为了区分成员变量,⼀般习惯上成员变量会加⼀个特殊标识,如成员变量前⾯或者后⾯加_ 或者 m 开头,注意C++中这个并不是强制的,只是⼀些惯例,具体看公司的要求。C++中struct也可以定义类,C++兼容C中struct的⽤法,同时struct升级成了类,明显的变化是struct中可以定义函数,⼀般情况下我们还是推荐⽤class定义类。因为我们刚从c转为c++有可能写着写着就将类转为结构体了。
注:如果执意使用struct来写类的话。需要记住我们在类内部定义成员变量的时候不需要再使用typedef了。我们可以直接写然后定义了如下面:
大家看到了吧,这里我们没有写typedef系统也没用爆红线出来。这就表明系统是支持这样的。这里大家需要注意一下,不要再像c语言那样还加一个typedef了。
好了。在类定义这个小节里面我们讲了,如何创建类,类成员可以是哪些。和如何使用类。以及类与c语言中的结构体的一些差异。
访问限定符
大家看到这里,就要来学习我们的第二个知识点了。访问限定符了。那么我们在前面稍微提及看一下访问限定符有那三个:public,private和protected。那么这三个词有什么用以及如何使用。是不是必须使用咧。这也是我们接下来要讨论的主题了。
我们对于这三个单词的作用我们可以先来看着三个单词的意思:public是公开的,private是私有的,protected是保护的。大家先想一下。这些单词的意思加上上面的标题大家思考一下这些的作用是什么。是不是与我们那个全局变量和局部变量很像啊。当用public修饰的类成员的时候这些成员都是可以在外部访问的。那么private和protected就是相反的只能在类里面访问了。但是大家可能会问。protected和private意思不是不一样嘛。确实。这两个单词意思是不一样的。但是要讲具体的区别的话,我们这里才学这么一点,是讲不清楚和不能理解的,所以关于protected和private的具体区别我们在后面对c++的知识有更多的了解后在具体解释。我们现在就需要知道这两个单词的作用差不多就可以了。public修饰的成员在类外可以直接被访问;protected和private修饰的成员在类外不能直接被访问。
那么访问限定符是不是在类里面必须使用啊。不用不行啊。当然不是啊。大家应该看到我上面的那张图片了吧。我创建的类也并没有写访问限定符啊。这就表明类不是一定需要访问限定符。依据使用情况使用。
我们还在前面说过struct与class都是创建类的关键词。那么他们的区别是是吧。区别就是class定义成员没有被访问限定符修饰时默认为private,struct默认为public。我们以下面的这个图片为例子,我们在这个类里面使用的struct。并且我们并没有用访问限定符来修饰,所以关于这个类,在外部是可以被访问的。就是我们以前说的是全局变量,是可以随意使用的。但是如果我们使用的是class的话,那么这个就是局部变量,外部是不能使用的。只能在类内部使用才行。
还有就是访问权限作⽤域从该访问限定符出现的位置开始直到下⼀个访问限定符出现时为⽌,如果后⾯没有访问限定符,作⽤域就到 }即类结束。就是没有被修饰的地方就是我们关键词的作用域,如果被修饰了那么作用域至到下一个限定符。如果没有下一个限定符,就直接到末尾了。
好,本小节我们讲了访问限定符的含义,使用,以及每个限定符的区别和作用域。
类域
不知道大家在以前的时候是否有过那种。就是不想定义和生命在一起的,就想我们前面声明头文件那样,声明和定义分开。
在c++中我们可以在类外面定义。什么意思咧。好比一个总部派了一个人在其他地方考察。如果可以直接在那里创建了一个分公司帮助总公司完成业务。但是我们创建子公司肯定要表明自己来自那个公司嘛。所以在类体外定义成员时,需要使⽤ :: 作⽤域操作符指明成员属于哪个类域。那么具体如何实施。我们看看下面的图片:
也就是简单的在主类里面声明。然后在自己想要的地方定义。只是我们需要确定好这个定义来自哪个类域。上⾯程序中Init如果不指定类域Stack,那么编译器就把Init当成全局函数,那么编译时,找不到array等成员的声明/定义在哪⾥,就会报错。指定类域Stack,就是知道Init是成员函数,当前域找不到的array等成员,就会到类域中去查找。
好吧,这一小节就是向为大家展示,类可以将定义与声明分开,不用在类内部全部写完。可以在外部进行定义。
类实例化
不知道大家对这个标题有没有什么见解。我们看题目大家或许有点了解吧。而且我们前面用过关于这个知识点的基础使用方法的。大家可以到上面看看我们说的那个boss的例子。⽤类类型在物理内存中创建对象的过程,称为类实例化出对象。 类是对象进⾏⼀种抽象描述,是⼀个模型⼀样的东西,限定了类有哪些成员变量,这些成员变量是声明,没有分配空间,⽤类实例化出对象时,才会分配空间。
这个大家应该还是很好理解的如⼀个类可以实例化出多个对象,实例化出的对象 占⽤实际的物理空间,存储类成员变量。打个⽐⽅:类实例化出对象就像现实中使⽤建筑设计图建造出房⼦,类就像是设计图,设计图规划了有多少个房间,房间⼤⼩功能等,但是并没有实体的建筑存在,也不能住⼈,⽤设计图修建出房⼦,房⼦才能住⼈。同样类就像设计图⼀样,不能存储数据,实例化出的对象分配物理内存存储数据。我们开始的类都是设计图。我们只有在使用的时候才会在编译的时候申请空间。我们简单的看一下下面的图片就能理解很多了:
是不是与我们上面的例子很像啊。只是我们上面的例子只用实例出了一个。这里我们出了2个并且住了不同的人。 并且传的数据一般都是不一样的。不然的话,你实例出两个干什么啊。是吧。
好,这个小节就是告诉我们在我们开始写类的时候只是在画图纸,并没有直接申请空间。只有当我们实例化编译的时候去申请空间。然后我们可以多个实例化。并且传入不同的参数。
类大小计算
大家应该还没有忘记c中的结构体计算大小的规则吧。我们在前面说过c++的类包含了c中的struct,让struct升华成了类。那么结构体可以计算大小。我们类肯定也要有。所以我们现在来看看如何计算类的大小。
首先分析⼀下类对象中哪些成员呢?类实例化出的每个对象,都有独⽴的数据空间,所以对象中肯定包含成员变量,那么成员函数是否包含呢?⾸先函数被编译后是⼀段指令,对象中没办法存储,这些指令存储在⼀个单独的区域(代码段),那么对象中⾮要存储的话,只能是成员函数的指针。再分析⼀下,对象中是否有存储指针的必要呢,Date实例化d1和d2两个对象,d1和d2都有各⾃独⽴的成员变量_year/_month/_day存储各⾃的数据,但是d1和d2的成员函数Init/Print指针却是⼀样的,存储在对象中就浪费了。如果⽤Date实例化100个对象,那么成员函数指针就重复存储100次,太浪费了。并且其实函数指针是不需要存储的,函数指针是⼀个地址,调⽤函数被编译成汇编指令[call 地址], 其实编译器在编译链接时,就要找到函数的地址,不是在运⾏时找,只有动态多态是在运⾏时找,就需要存储函数地址,这个我们以后会讲解。我们现在再讲讲内存对齐的规则:
首先我们看看,第一个例子是我们最常见的有成员变量和函数。但这里我们只看变量。先char后是int那么大小是不是刚好为8啊。这个简单。但是我们看后面这两个例子就有古怪了。b没有成员变量啊。还有c啥都没有啊。这里我们就需要牵扯出外面c++这里计算大小的 专属规则了。
大家想一想,如果这里我们给0的话。那就代表我们这里没有大小啊。没有大小不就表明啥都没有。没有就是无,调用无。会不会报错啊。所以我们不能给0。所以这⾥给1字节,纯粹是为了占位标识对象存在。
好吧,这里咧我们计算类大小与计算数据结构大小是差不多的,区别就是当类没有变量的时候也会给个1。表示来过。
this指针
看到指针大家应该很激动吧。我们以前有过学习,那么我们这里学起来就很简单了。对于这个知识点的引入大家可以思考一下Date类中有 Init 与 Print 两个成员函数,函数体中没有关于不同对象的区分,那当d1调⽤Init和Print函数时,该函数是如何知道应该访问的是d1对象还是d2对象呢?是吧,编译器怎么知道用的是哪个。所以c++中给了⼀个隐含的this指针解决这⾥的问题。那么这个this具体是如何解决问题的咧。
其实编译器编译后,类的成员函数默认都会在形参第⼀个位置,增加⼀个当前类类型的指针,叫做this指针。⽐如Date类的Init的真实原型为, void Init(Date* const this, int year, int month, int day)。意思是是说在外面的参数接受的时候其实是有一个隐藏的参数没有写的。这个参数就是来解决应该用哪个函数的。
并且类的成员函数中访问成员变量,本质都是通过this指针访问的,如Init函数中给_year赋值, this->_year = year;所以大家明白为什么系统不会搞混了吧。就是我们的参数中是有一个隐含的this指针来指定该用那个函数。不用我们特别指定,不然的话,我们又会多很多工作量。但是C++规定不能在实参和形参的位置显⽰的写this指针(编译时编译器会处理),但是可以在函数体内显⽰使⽤this指针。就是说,在传参和接参的时候不能写,因为编译器会给我们写的。但是在内部可以写,毕竟都是公用的所以可以写。但是你不写也是可以的毕竟都在内部了,要是不特别指定的话,都是用内部的数据。
ok,这个小节就是告诉了我们在外面传参的时候有一个隐含的this指针,这个来表明我们引用的是那个函数。这样我们系统就不会乱调用了。但是我们不能直接写出来只能让系统帮我们写。但是可以在函数体内显⽰使⽤this指针。就像我们上面图片中的那样。
总结
好了,上面的内容就是我们今天这篇博客的知识了。关于c++的类的话这还只是马前卒。大家可以看到这篇博客有很多字。所以需要大家理解记忆的地方很多。大家多看看。也可以去看一些视频讲解。大家如果想要我博客中的代码的话,大家加油在Gitee中搜索@yang-hong-and-duan。最新的那个里面就有了。