(上篇)
一、面向对象的认识
1、面向对象是什么?
完成一件事情所需要完成的过程通过对象间的交互进行完成,例如外卖平台中可以认为是通过用户、骑手、商家之间的交互进行完成的,用户点单——商家接单、制作——骑手配送
2、面向对象和面向过程的区别
通过字面意思,将一件事情的完成,通过不同的角度去进行分析和完成,面向对象是将事情完成的角度侧重于与这件事情有关的对象进行交互;面向过程则是将事情完成的重点放在过程上面,通过分析事情的步骤方法去进行。
举个栗子:
要完成洗衣服这件事情,我们可以分别从面向对象和面向过程进行分析。
首先从面相过程的角度进行分析
拿衣服→放水→放洗衣液→洗衣→拧干→晾晒
从面向对象角度进行分析
对象有:人、衣服、洗衣机 三者之间通过交互进行
二、类的引入
在C语言中,结构体中只能定义变量,并不能定义函数,以至于函数需要定义在结构体外,传参相对来说还是比较繁琐,C++中引入类的概念,在类中既可以包含变量,还可以包含函数,一定程度弥补了C语言的繁琐
三、类的定义
1、概况
一个类需要 class关键字、类的名字、{};
类中包括类的属性(成员变量)和类的方法(成员函数)
2、类定义的两种方法
-
声明和定义不分开--------(声明和定义全部放在类体中)
这里需要注意,成员函数声明和定义全部在类体中时,编译器可能会将成员函数当成内联函数进行处理 -
声明和定义分开--------(声明放在.h文件中,定义放在.cpp文件中)
- 注:建议使用第二种
- 注:建议使用第二种
四、类的访问限定与封装
1、访问限定符
访问限定符有三种
- public (公有)
- private (私有)
- protected (保护)
访问限定符的几点说明
①public是公有,是允许在类外进行访问的
②private和protected都可以看成私有,二者没啥明显差别,只允许在类内进行访问
struct 和 class的区别
struct中默认访问限定时public,而class中默认限定是private
2、封装
封装为面向对象的三大特性之一
封装的概念
将数据和数据的方法进行有机结合,将私有数据进行隐藏起来,仅开放公有接口对外进行交互
封装的目的
本质上是一种管理,让用户更好的使用类
五、类的作用域
类定义了一个新的作用域,类的成员变量和成员函数都在类的作用域中,当类外需要访问类中的成员时,需要通过 :: 进行访问
六、类的实例化
用类创建对象的过程就称为类的实例化
定义和声明并不会为类中的成员开辟空间,类的实例化的作用就是为类中的成员开辟空间
一个类可以实例化多个对象
七、类大小的计算
在未学习类的大小计算之前,我们总会下意识觉得一个类的大小应该是成员变量和成员函数的总和,但实际并不是这样
一个类的大小实际就是成员变量的大小,根据内存对齐原则
八、this指针
对于this指针的理解
当我们进行一个类进行多次实例化时,将实例化出来的对象进行调用类中的成员函数,如何将对象和其调用的成员函数进行匹配?
c++通过引入this指针进行解决,这里就是this指针的功能,编译器增加了一个隐藏的指针在成员函数的形参位置,让指针指向当前对象,这个指针比较特殊,不需要用户去进行传递,而是由编译器进行传递的。
this 指针的特性
- this的类型为 *const
- this指针只能在成员函数的内部进行使用
- this指针本质上是成员函数的形参,将对象的地址作为实参传递给形参this指针
- this指针是成员函数隐形的第一个形参,一般情况下通过编译器ecx进行传递,不需要用户进行传递
---------------------------------------------------------------------------------------------------------------------------------
(中篇)
九、类的六种默认成员函数
- 构造函数
- 析构函数
- 拷贝构造函数
- 赋值重载函数
- 取地址
- const成员取地址
什么是默认成员函数?
默认成员函数就是如果自己不定义,就会调用编译器自动生成的函数
十、构造函数
我们思考,每当进行类的对象实例化,都需要我们手动进行赋值初始化,有没有当对象实例化出来便自动调用某个函数进行自动初始化的功能呢? 有的,构造函数就抗起来了这种责任。
1、功能
构造函数主要用于完成初始化工作
2、特性
- 函数名与类名相同
- 无返回值
- 可以构成函数重载
- 对象实例化出来时自动调用构造函数
- 如果没有显式定义构造函数,C++编译器会自动生成一个无参隐式默认构造函数,类中一旦定义显示构造函数,那么编译器将不在自动生成默认构造函数
- 默认生成的构造函数对内置类型是不做处理,对自定义类型的成员会调用他的默认构造函数
注:C++11针对默认生成的构造函数对内置类型不赋初始值打了个补丁,内置类型在声明的过程中可以给默认值 - 无参、全缺省、默认生成的都称为默认构造函数
附代码实例
十一、析构函数
1、功能
对象在销毁时自动调用析构函数,用于资源的清理
2、特性
- 函数名是类名前面+ ~
- 无返回值无返回类型
- 一个类中只能有一个析构函数(不支持函数重载),当类中没有显示定义析构函数,编译器会自动生成一个析构函数
- 对象生命周期结束时自动调用析构函数
- 如果类中没有资源申请时,析构函数可以不写,使用编译器自动生成的默认析构函数,如果有资源申请一定要写,防止内存泄漏
附代码
十二、拷贝构造函数
内置类型赋值拷贝非常简单,那么自定义类型赋值拷贝又该何去何从呢?
C++引入拷贝构造函数进行赋值拷贝
1、功能
在创建对象时,通过已创建的对象进行赋值,创建一个与已存在对象一某一样的新对象这里已创建对象必须和即将创建的对象是同类
2、特征
- 属于构造函数,和构造函数构成函数重载
- 构造函数的形参只有一个且是传引用的方式,若是传值调用则会无穷递归
- 若没有进行显示构造,编译器会自动生成一个默认构造函数
- 自动生成的默认拷贝构造函数进行拷贝是按照字节数进行拷贝,是属于浅拷贝
- 当类类中没有涉及到资源的申请时,拷贝构造函数写不写都可以;当类中涉及资源的申请时,必须自己写拷贝构造函数,否则就是浅拷贝
3、分析当类涉及资源申请时调用浅拷贝的影响
浅拷贝是按照字节进行拷贝,两个类中申请资源会出现矛盾
以栈为例
4、拷贝函数典型调用场景
- 使用已创建的对象去赋值新创建的对象
- 自定义类型作为函数的参数进行传参
- 返回值为自定义类型的临时拷贝构造
这里实际temp还是会掉一次析构,被编译器优化了
十三、赋值运算符重载
1、运算符重载
C++为了增强代码的可读性,赋予符号在类上的特性,引入运算符重载的概念,通过关键字operator使得类也能够进行相对应的运算。
注意事项:
①必须含有一个类类型的参数
②五大不能运算符重载的运算符 .* . :: ?: sizeof
③内置类型的运算符含义不能更改
代码随想:
对于判断运算(==、>、>=、<、<=)只需要写任意两个就可以推导其余三个,这里推荐使用==和其中一个进行组合,代码觉简单
由于_year _month _day都属于私有,为了调用_year等用了友元函数
2、赋值运算符重载
2.1赋值运算符重载的格式
- 参数类型为const T&
- 返回值为T&
- 检查是否能自己给自己赋值
- 返回 *this 要满足连续赋值的含义
2.2赋值运算符重载的特性
- 赋值运算符只能存在于类内,若没有显示定义,编译器会自动生成一个隐式默认赋值运算符,若此时在全局再进行定义,会与默认赋值运算符冲突从而报错
- 默认生成的赋值运算符,赋值原理是按照浅拷贝进行赋值,因此对于内置类型来说,赋值运算符的定义不需要自己写,对于自定义类型来说,赋值运算符的定义一定要自己写
2.3前置++和后置++重载
代码随想
前置++和后置++在实现上有哪些区别
前置++和后置++在实现上其实就是函数重载,后置++ 参数就是为了构成函数重载;从效率上来看前置++更好,后置++有两处需要调构造函数(temp的创建和返回值),效率相对来说更差
十四、const成员函数
const成员函数就是由const进行修饰的成员函数,被修饰的成员函数不可被修改,本质实际上修饰的是this指针
思考
1. const对象可以调用非const成员函数吗?
2. 非const对象可以调用const成员函数吗?
3. const成员函数内可以调用其它的非const成员函数吗?
4. 非const成员函数内可以调用其它的const成员函数吗?、
小结:
根据const对象权限只能缩小,不能扩大的原则,const对象可以调用非const成员函数;非const对象不可以调用const成员函数; const成员函数内不可以调用其它的非const成员函数;非const成员函数内可以调用其它的const成员函数
十五、取地址及const取地址操作符
这两个函数一般不需要重新定义,系统会自动生成
Data*operator&()
Data*operator&() const
(下篇)
十六、再谈构造函数
1、初始化列表
初始化列表的格式:
①:起手 ②成员变量之间用 , 进行间隔 ③复制成员放在()
初始化列表和构造函数体进行赋值的区别:
---为什么已经有构造函数体赋值还需要初始化列表
- 构造函数体进行赋值可以进行赋值很多次不能称之为初始化,而通过初始化列表进行赋值只能进行一次赋值
- 以下情况必须使用 引用、const成员、自定义类型成员(该自定义成员没有默认构造函数)
引用和const成员在定义时需要进行赋初值,防止自定义类型成员(该自定义成员没有默认构造函数)进行浅拷贝赋值 - 成员变量初始化顺序由声明顺序决定
2、explicit关键字
什么是隐式类型转化?
像这样aa1 是自定义类型A 为什么整形2能够给aa1进行赋值? 这之间其实是发生隐式类型转化,其原理是中间生成了一个以A为类型的中间变量通过构造将2的值赋给中间变量,然后中间变量通过拷贝构造赋值给变量aa1
功能:用于防止隐式类型转化
十七、static成员
1、概念
声明是static类成员,static进行修饰,分为成员变量和成员函数,static必须在类外进行初始化,初始化时不用带static
2、特性
- static成员为类中所有对象所想,不属于具体某个对象,存在于静态区
- 静态成员变量必须在类外定义,定义时不添加static关键字,类中只是声明
- 类静态成员即可用 类名::静态成员 或者 对象.静态成员 来访问
- 静态成员函数没有隐藏的this指针,不能访问任何非静态成员
- 静态成员也是类的成员,受public、protected、private 访问限定符的限制
3、经典题型
十八、友元
1、友元函数
当一个函数定义在类的外面,该函数还需要访问类中的私有成员,就可以通过友元函数就打破封装,从而获得访问私有成员变量的资格
有关说明:一个类可以有多个友元函数
利用友元函数实现自定义类型的流插入和流提取去实现自定义类型的输入和输出
附代码
2、友元类
- 友元类只是单向的
- 友元类不能传递
- 友元类不能继承
十九、内部类
1、概念:
一个类定义在另一个类内部,这个定义在内部的类就叫内部类,内部类是外部类的友元,也就是说,内部类中可以访问外部类中的私有成员。
2、特性:
- 内部类即可以定义在公有,也可以定义在私有,不受限制
- 内部类可以直接访问外部类中的static成员变量
- sizeof(外部类)和里面内部类没有关系