【C++初阶】:类与对象(上)

一.面向对象的初步认识

C语言是面向过程的,关注的是过程,分析出求解问题的步骤,通过函数调用逐步解决问题。

例如洗衣服

在这里插入图片描述

而C++是基于面向对象的,关注的是对象,将一件事情拆分成不同的对象,靠对象之间的交互完成。

在这里插入图片描述

二.初步认识类

C语言结构体中只能定义变量,在C++中,结构体内不仅可以定义变量,也可以定义函数。比如:之前在数据结构初阶中,用C语言方式实现的栈,结构体中只能定义变量;现在以C++方式实现,会发现struct中也可以定义函数

这里是c语言实现的栈传送门,通过比较就可以知道为什么会出现类的概念了。

在这里插入图片描述
在这里插入图片描述

上面的struct stack就是一个类,可以把一个栈的多个功能写在一个花括号内。在c++中,上面的struct更喜欢用class替代。

三.类的权限

我们对上面的代码进行改进,将struct改成class。

在这里插入图片描述

但是编译器直接告诉我不可访问,为什么struct可以直接编过而class却不能呢?这就涉及到类的权限问题了。

访问权限分为三种
在这里插入图片描述

1. public修饰的成员在类外可以直接被访问
2. protected和private修饰的成员在类外不能直接被访问(此处protected和private是类似的)
3. 访问权限作用域从该访问限定符出现的位置开始直到下一个访问限定符出现时为止
4. 如果后面没有访问限定符,作用域就到 } 即类结束。
5. class的默认访问权限为private,struct为public(因为struct要兼容C)

插个小帖子,现阶段认为私有和保护是一样的,直到继承时才进行区分。

简单来说,如果你想要函数能在外部被访问,就需要加上public

在这里插入图片描述

那么public的作用到哪里结束呢?直到遇到下一个限定符或者结束。

在这里插入图片描述

上面的public作用到private结束,private向下直到类结束。

总体来说struct和class是没有什么区别的,最大的不同就是struct默认权限是公有,class默认情况是私有,所以上面的代码直接将struct换成class是不可访问的。

四.类的声明和定义

类也可以声明和定义分离,那么是如何做到的呢?现在将上面的代码改造一下。

在这里插入图片描述

我们这里将init函数踢出,只在类里面声明,在fun.cpp里定义。

在这里插入图片描述

这样就可以实现类里函数声明和定义的分离。注意每一个类都是一个作用域,所以在定义时要加上域名,不然就会出现下面无法识别的问题。

在这里插入图片描述

这里编译器就不知道去哪里找_array,_size…符号。

那么我们可以在类里声明内联函数吗?

在这里插入图片描述

答案是不可以,内联函数的声明和定义是不可以分开的,具体原因大家可以看看这篇博客传送门

注意:在类里直接定义的函数会被默认为内联函数(这是c++的规定),所以如果一个函数过长,我们通常将其声明和定义分离出来。当然如果我们直接在类里定义了一个很长的函数,那么它真的就会变成内联函数吗?其实也不是,具体原因可以看看这篇博客传送门

顺便插一个不好的变成习惯

在这里插入图片描述

这里右边的year其实是init的参数year,因为优先使用局部域。private里的year是出于类域的。这里编译器不会报错,但很影响代码的可读性。

五.封装

在这里插入图片描述

在这里插入图片描述

在c语言中可以直接通过修改_array来达到目的,但这种操作是不规范的。在c++里我们将其设置为了私有,那么就可以强制程序员去调用函数来修改_array,规范了变成。

六.类的实例化

在上面的代码里,我们可以很明显的看到Init函数是声明,push,top…函数是直接在类里定义。那么下面的_array,_size…变量是声明还是定义呢?

在这里插入图片描述

答案都是声明。变量的声明和定义在于是否开辟空间。

在这里插入图片描述

这些变量并非一个一个定义的,而是一起定义的。而定义这些变量的过程就被称为类实例化对象或者对象定义。

在这里插入图片描述

这里举个例子,如果我们将pravate删掉,意味着这些变量可以直接访问,那么我们能直接赋值吗?

在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

很明显是不能的,类就像是一张图纸,你很明显不能在图纸里放上一把椅子或者桌子,你必须依照图纸将这个房子的空间建出来,才能够往里面放入家具。

七.类的大小

我们创造了一个对象s,那么这个类的大小是多少呢?

在这里插入图片描述

在这里插入图片描述

答案是12。结论是对象中只储存成员变量,不储存成员函数。注意:就算时也要像struct一样进行内存对齐。如果不太了解的可以看看这篇传送门

在这里插入图片描述

我们应该怎样理解这个现象呢?其实也很简单,我们再创立一个对象。

在这里插入图片描述

两个对象的_size是一样的吗?很明显不是的。如果我们类比一下,就好像变量是每个房子的厕所,厨房…每个房子都要这些空间并且是独立的。而函数就像小区的花园,篮球场,多户人共用一个就可以了。(当然也可以每户人都建一个花园,但很明显太浪费了)

在这里插入图片描述

总结:每个对象所建立的对象都是独立的,而函数的调用都是同一个函数。

那么我们接着往下看

在这里插入图片描述

这里的A2和A3的大小是多少呢?(ps:这里直接对类求大小和对对象求大小是一样的)。答案都是1

在这里插入图片描述

为什么都是1呢?那么我可以问一个问题,如果都是0字节,那么下面的两个对象如何区分呢?

在这里插入图片描述

在这里插入图片描述

如果它们是0字节的话,就不会有地址,那么这个类就不能用了。

所以,没有成员变量的类对象,需要1字节,用来占位,表示对象存在,不存储有效数据。

八.this指针

1.特性

在这里插入图片描述

在这里插入图片描述

大家看上面一段代码,为什么两个打印是不一样的呢?前文我们说到(这篇博客的类的大小部分)类里的函数都是存放在一个公共区域内,所以不同对象调用的都是同一函数,那为什么结果还不一样呢?这里就要提到this指针了。

在这里插入图片描述

可以这样理解,实际上编译器在编译后把函数改成了下面的样子。

在这里插入图片描述

在这里插入图片描述

实际上就是编译器“暗箱操作”,悄悄的改了函数。

2.存储位置

this是形参也就是实参的一份临时拷贝,所以this指针其实是作为栈帧的一部分存在栈里的(如果对栈帧不太了解可以看看这篇博客传送门)。所以在函数调用结束后this就被销毁了。

下面看道例题

在这里插入图片描述

这段代码能成功运行吗?

在这里插入图片描述

答案是可以的,因为虽然p是空指针,但printf函数里并不需要参数,也就是说不需要对this指针进行解引用,那么自然就能正常运行了。

那么我们可以使用::去直接访问Print函数吗?

在这里插入图片描述

答案是不能的,因为这样写就相当于直接丢掉this指针,即使this指针是空指针也依然要传过去。

那么我们可以直接这样写吗?

在这里插入图片描述

答案是不能,因为this指针能在函数里显示使用但不能显示传递,这是语法规定。

猜你喜欢

转载自blog.csdn.net/m0_73790767/article/details/130289614