面向对象关注的是一个操作需要哪些对象完成。
类是数据(变量等)与操作(函数)的集合。
(类是对象的图纸)
在C++中,struct也可以内含成员函数。
调用时,变量名.成员-->与C中结构体使用相同,不过可以不用再写struct了。
在C++中,我们更习惯用类,而不是兼容C的结构体
类的关键字:class
使用:class 类名{类的主体};
class表明这是一个类,类名用于区分不同类,代码块内部为类的主体为成员定义/声明(等会会讲声明),别忘了和结构体一样加分号。
类体中内容称为类的成员:类中的变量称为类的属性或成员变量; 类中的函数称为类的方法或者 成员函数
C++中class与struct区别?
1.不同: 1.struct可以定义结构体(兼容C),
2.class默认访问权限是private(所以不好定义结构体(加public)),struct默认访问权限是public。
2.相同:struct,class都可以定义类
声明和定义:重点在于成员函数定义和声明:
1.直接在类的主体中定义-->可能编译器会将其当作内联函数处理。
如果调用函数,汇编会用call调用函数地址。
2.声明与定义分离-->在别处定义(可以不同文件),定义时要指定类域-->成员函数名前要加类名。(硬性要求)。如果成员函数使用了成员变量,让类先定义,才有变量,成员函数在其定义之后定义。
规范:成员变量前加_和成员函数的形参区分
访问限定符初识:
类是方法与属性(数据)的封装,如果外部可以随意调用,就可能会引起一些不必要的由程序员造成的问题,那么,限定类中成员的访问(专业说是选择性的给外部提供接口)则是安全又高效的做法。
类别:
C++有三种访问限定符:public(公用),private(私有),protected(保护).
说明(规则):
1. public修饰的成员在类外可以直接被访问
2. protected和private修饰的成员在类外不能直接被访问(此处protected和private是类似的)
3. 访问权限作用域从该访问限定符出现的位置开始直到下一个访问限定符出现时为止
4. 如果后面没有访问限定符,作用域就到}即类结束。
5. class的默认访问权限为private,struct为public(因为struct要兼容C
本质:
访问限定符只在编译时有用,目的是通过语法限定访问。
类的封装:
1.封装定义:封装指将数据与操作结合,并隐藏部分属性(数据)与细节(操作),仅提供部分接口给外部访问。
2.类的封装的实现:类中存在类域,成员作用域都在类域中,要调用要指定哪个类域。(与命名空间相同,都是对成员名字的隔离,防止名字冲突,实现指定访问,也保证了不会被随意调用)。
类的实例化:
首先,类是没有大小的,类仅仅相当于图纸,与C中结构体类型同。
所以,我们定义了一个类,仅仅为对象提供了一种实现方式。
而通过类定义一个对象,实实在在开辟空间的过程,叫类的实例化。
类(对象)的大小:
1.对象中,类定义的函数是没有大小的。
因为类与对象是1对多的关系,所以编译器让操作系统在全局区为函数开辟空间,且一个类的一个函数只开辟一次。(节省空间)
定义几个对象,他们都调用了同一函数地址。
this指针
那每个对象使用同一函数结果不同原因是每个对象传了自己的地址,给函数形参暗含的this指针。
如图:每个成员函数参数其实都默认有个不能改指向的this指针,但在参数部分不能写明,因为这是编译器处理的事,内部明着写或不写,都可以用,因为编译器处理好了。
问:1.this指针在哪里:在栈上或者寄存器上
2.this指针可以为空吗?
一个正常对象的地址不会为空,但可以定义类型为此类的指针,在通过指针找到成员函数并传给它。
其实是通过p指针调用了成员函数,成员函数接受了此类指针后确定是此类,能访问,再执行打印,由于打印没用到对象内部,不需解引用,也就不会出错。
(过程:把A类型变量*p地址传给能调用的print,this=p=nullptr,使用print成员函数)
下面的访问了对象内部,解引用了空指针,就会运行出错(语法出错是没有相应对象,c++代码书写有问题,不是标准定义的;(比如 int写成了in,漏写个分号,漏写个括号,把int型参数赋值给字符串,等等),一般语法错误在编译时都是可以被编译器发现,发出警示),或类型错误,运行出错指出现未定义行为如解引用空指针。)
2.内存对齐-->类的成员变量存在内存对齐。
对齐数=编译器默认对齐数与该类型大小的较小值。
总大小:最大对齐数的整数倍
开头变量默认对齐到0。
让其不对齐:更改编译器默认对齐数:#pragma pack(n)
为什么要内存对齐:CPU是按其地址总线大小的整数倍读取的,如32位,4字节,如果一次读取就是一个变量数据那效率高,但可能显示char1字节和int的前3个字节,在一次读Int的最后字节,再拼接起来。
3.sizeof()可加类名或对象,都是测类型的大小。
当sizeof()测只有成员函数或没有成员的类(对象)时,每个编译器会返回不同的值,表明已有此类或此对象已经初始化。
类的6个默认成员函数:
当一个类中什么都不写,称为空类,但其实空类中仍有6个默认的成员函数,是编译器在你不写他时自动帮你生成的。
1.初始化和清理
1.构造函数
主要用于对象成员的初始化。
1.规则:
1.名字与类名相同,
2.无返回值,
3.可重载,(半自动)
4.类实例化(对象初始化)时自动调用此函数,
5.构造函数无参数时,对象初始化不用加括号(以免和函数声明格式冲突)
6.其应被public访问限定符修饰,表明外界初始化对象时就能访问。
7.对象生命周期内只能使用一次。
2.使用:
1.一般设定为全缺省函数,将对象全按默认或输入的初始化一遍。
注意:全缺省初始化不传参时,不能加括号,防止和函数声明冲突。
即:对象初始化时不能加空括号,不然认为是函数声明。
3.自动化:
编译器在你没写构造函数时,会为你加一个,功能为:
1.可能处理你的内置类型变量(内置类型为语言自带的类型,如:Int,double...)
有的编译器会给你的内置类型变量赋0,有的不会动,所以里面还是随机值。(太假了,这个功能竟然还看编译器)。
2.调用自定义类型变量的构造函数(层层调用)。(如果你的成员自定义类型变量中有自己写的构造函数,这至少可以让这个变量内部成员初始化)。
应用:两个栈实现一个队列,类中放两个栈,类实例化时,栈内部都初始化了。
2.析构函数
析构函数:与构造函数功能相反,析构函数不是完成对对象本身的销毁,局部对象销毁工作是由 编译器完成的。而对象在销毁时会自动调用析构函数,完成对象中资源的清理工作。(销毁成员变量)
1.规则
1. 析构函数名是在类名前加上字符 ~。
2. 无参数无返回值类型。
3. 一个类只能有一个析构函数。若未显式定义,系统会自动生成默认的析构函数。注意:析构 函数不能重载(全自动,没地方给你加括号传参调用)
4. 对象生命周期结束时,C++编译系统系统自动调用析构函数
5.需要public修饰,结束时才能在外界自动调用。
2.使用:
自己写个析构,在对象生命周期结束时自动销毁动态申请的空间。
3.自动:
如果对象内部有自定义类型变量,编译器会先调用自定义类型变量的析构,再调用当前类的析构(层层销毁返回)
注意:默认的析构函数不会销毁动态申请的空间,需要你自己写!
类的使用:
一般类直接在头文件里定义,类型定义不会导致重复定义错误。