目录
前言
前面浅浅喝了一小口汤,我们现在就再认识一下c++在编写程序时一些和c语言不同的地方,c++是升级版,包含c的同时又做了什么比较好的优化呢?
类和对象
我们之前写结构体的时候,比如说写栈http://t.csdnimg.cn/JveMq ,是不是像这样啊
一个头文件包括每个函数的声明还有栈结构体的创建,一个.c文件放我们函数的定义,一个.c文件用来测试我们的程序,要哪个功能就调用哪个函数。
因为c++是包含c的,所以我们继续这样写也问题不大,我们看看c++是怎么写的
C语言中 struct ListNode是结构体类型,C++是把结构体升级成了类,也就是说,结构体是类的一部分,类除了是结构体,还可以在结构体中放我们的函数。
这样写就是类的写法了,就是把我们的结构体,还有我们的函数都包含在一块,实现的时候直接在类里面就实现了。类是将函数包含在结构体当中去实现,在c++中,类的函数称为成员函数,类的结构体变量称为成员变量。
c++中,类的结构一般写为class,struct也可以写,只是不怎么这样写。class将成员称为属性,函数称为方法。
除了直接将成员函数和成员变量放在类里面,c++也支持类外编写成员函数,这里的话需要加上我们的域作用限定符
class限定符
c语言中,数据的访问控制自由,不受限制。c++中,类的访问分为三种:公开,保护和私有。公开是指可以在类外访问类属性,保护和私有则是在类里面访问。
此时public里面就是可以在main函数里面去访问到的方法 ,private就是私有控制形式,就访问不到,程序会提示错误。
这里变量加_是为了更好的编写我们的程序,如果是year=year,我们的成员变量是year,我们的方法参数也是year,是不是就会引发不必要的错误啊。
加_以后就会保证程序的正常运行,防止名字相同导致错误。
公开还是私有取决于我们希不希望别人修改我们的变量。
实例化
实例化的意思就是我们的类对象在运行时有没有开辟空间。比如说,我们手里有一份建房图纸,我们准备用它来建我们的房子,但是我们还没建,就不是实例化,我们开始建了,用了这块地,就是实例化。实例化就是用我们的图纸(类)建造一栋栋房子(空间)。
上面这个类占我们多大的内存呢?int是4个字节,我们有三个,那就是12.我们的方法占不占我们的内存空间呢?我们运行一下看看什么样子。
12,也就是说我们的方法是不占内存空间的,实际运行中主要是看我们的成员变量。在实例化中,对象大小遵循内存对齐原则,实际大小取决于成员变量。
我们再看一下下面这个
我们上面那个代码是成员变量占内存空间,成员函数不占。那现在这两个类在内存中会开辟空间吗? 它两都没有成员变量,一个有成员函数,一个什么都没有,它们运行起来是多少呢
咦,怎么是1啊,在我们程序中,一个成员函数是不占我们的空间的,因为它是属于我们的公共区域,存在在代码节中。什么是公共区域呢,比如说,我们住在小区里面的一栋房子里,然后这个小区有配套游泳池,篮球场。那这些区域是不是公共的啊,实际我们购买的只是我们的房子,篮球场我们有使用权,但没有拥有权,对吧。占空间的是房子(变量),不是篮球场(函数)。
为什么成员变量在对象中,成员函数不在对象中呢?
每个对象成员变量是不一样的,需要独立存储
每个对象调用成员函数是一样的,放到共享公共区域(代码段)
当我们的函数没有变量时,我们的函数存放在代码节中,本身是不占空间的,没有变量,就会在内存中占1个字节,表示我们的对象实例化出来了。
大小是1,这1byte不存储有效数据。实际作用只是占位,标识对象被实例化定义出来了。
this
我们d1调用了类的初始化功能,但它是怎么知道我们的d1是d1,d2是d2呢,这里面是怎么辨别我们传的这个参数指向的对象呢?
C++中通过引入this指针解决该问题。
即:C++编译器给每个“非静态的成员函数“增加了一个隐藏的指针参数,让该指针指向当前对象(函数运行时调用该函数的对象),在函数体中所有“成员变量”的操作,都是通过该指针去访问。只不过所有的操作对用户是透明的,即用户不需要来传递,编译器自动完成。
this指针其实是编译器自己识别我们接收对象的工具,不能修改也不需要我们去加,如果加了会编译不过。也就是说this具有常属性,上面的就是Date* const。
this指针本质上是“成员函数”的形参,当对象调用成员函数时,将对象地址作为实参传递给this形参。所以对象中不存储this指针。
this指针是“成员函数”第一个隐含的指针形参,存储在栈中。一般情况由编译器通过ecx寄存器自动传递,不需要用户传递
只能在函数内部使用,我们可以打印this运行时的地址。
再来看一下下面这个情况
这几种情况下,我们的程序是正常运行呢还是编译报错或者说运行崩溃啊?
我们刚刚说编译器会this确认我们的对象,那它遇到空指针会怎么处理呢
我们先试一下第一个情况
可以看到,我们的ptr是空指针,程序在尝试写入的时候发现无法写入,所以报错了,因为我们的指针是空的啊,所以是放不了值的,这属于是典型的跨权限访问了。
再看一下下面两个。
两种情况,我们的编译器都正常通过了,为什么呢?我们的指针是空指针,但是func函数里面只是打印我们this接收到的东西,也就是空指针,然后输出一下func(),并没有尝试修改我们的对象,空指针是无法被修改的,所以这里不会报错。
第一种ptr传过去嘛,然后是空,编译器就直接打印了,这是没有问题的,第二个*ptr为什么也没事呢,按理说我们对空指针解引用的话,是会报错或者崩溃的吧。原来,我们的编译器很聪明,它识别到了我们的对象是空指针,所以没有解引用,而是直接打印出来了,属于是一个意识到了,然后对我们的错误进行调整,才没有报错。日常编码中还是要避免这么书写。
也就是说,空指针是没有写入修改的访问权限的,我们记住这一点就可以了。
封装
接着说一下封装,封装的话有点像我们平时使用的电脑,里面有cpu,芯片,电池,电路,主板等等高精度的零件,然后用我们的机箱包装起来,开机的时候只需要按一下开机键,电脑就会自动开机了。如果没有封装,我们要启动的话是不是要一个个去操作让它运行起来啊?
所以说,封装的本质是为了更好的管理我们的程序,把我们的细节,一些小的功能藏起来,让整个程序更加方便管理。
封装:将数据和操作数据的方法进行有机结合,隐藏对象的属性和实现细节,仅对外公开接口来和对象进行交互。
在C++语言中实现封装,可以通过类将数据以及操作数据的方法进行有机结合,通过访问权限来隐藏对象内部实现细节,控制哪些方法可以在类外部直接被使用。
这个大家先了解一下即可,在后续学习过程中我们还会不断加深对封装等特性的理解。
水面上水面下
这个是我自己认为的定义,就是c和c++在数据处理方面的不同,c是把数据都放在水面上,大家都看得到,都有权限去操作和修改。c++是把数据放在水面下,用类把数据和方法都隐藏起来,用户只能修改公开的权限的方法和数据,没办法对受保护和私有的数据和方法进行权限访问。
这里的话 我们访问栈顶元素,因为不受限制,我们可以直接int top = st.a[st.top-1].去访问栈顶元素,但是这里的自由,是不是有点过了火啊?如果我们的栈顶是空呢?这个时候访问是不是引发程序报错啊,所以我们平时写函数会加assert进行判断栈是不是空,避免错误。
c++的话成员变量和成员函数都封装在类里面,访问的时候受到限制,只能访问写入公开权限的成员变量和成员函数,加上控制就使得更加安全一些,所以说c++更像水面下的数据处理方式。
今天的学习就到这里吧~很多地方我会就是比较简短的表达,如果有不对的地方可以私信我修改喔,一起学习,共同成长!