类和对象
c++面向的对象的三大特性为:封装、继承、多态
c++认为万事万物都皆为对象,对象上有其属性和行为
具有相同性质的对象,我们可以抽象称为类,人属于人类,车属于车类
封装
封装的意义:
将属性和行为作为一个整体,表现生活中的实物
将属性和行为加以权限控制
封装意义一:
在设计类的时候,属性和行为写在一起,表现事物
语法:class 类名 {访问权限 属性/行为}
//类中的属性和行为 统一称为成员
//属性 成员属性 成员变量
//行为 成员函数 成员方法
封装意义二:
类在设计是,可以把属性和行为放在不同的权限下,加以控制
访问权限有三种
1、public 公共权限 类内可以访问 类外可以访问
2、protected 保护权限 类内可以访问 类外不可以访问 儿子也可以访问父亲中的保护内容
3、private 私有权限 类内可以访问 类外不可以访问 儿子不可以访问父亲的私有内容
struct和class的区别
struct默认权限是公共public
class默认权限是私有private
//将成员属性设为私有
//1、可以自己控制读写权限
//2、对于写可以检测数据的有效性
构造函数和析构函数
构造函数 进行初始化操作
1、没有函数值 不用谢void
2、函数名 与类名想用
3、构造函数可以有参数,可以发生重载
4、创建对象的时候,构造函数会自动调用,而且只调用一次
析构函数 进行清理的操作
1、没有返回值 不写void
2、函数名和类名相同 在名称前加~
3、析构函数不可以有参数的,不可以发生重载
4、对象在销毁前,会自动调用析构函数,而且只会调用一次
构造函数的分类
按照参数分类 无参构造(默认构造)和有参构造
按照类型分类 普通构造 拷贝构造
注意事项:
person p1;调用默认构造函数的时候,不要加()
//因为下面这行代码,编译器会认为是一个函数的声明,不会认识在创建对象
//person p1();
不要利用拷贝构造函数 初始化匿名对象 编译器会认为person(p3) === person p3;
使用方法
括号法
person p2(100);
person p3(p2);
显示法
person p2 = person(10);
隐式转换法
person p4 = 10;//相当于写了person p4 = person(10);
构造函数调用规则
默认情况下,c++编译器至少给一个类添加三个函数
1、默认构造函数(无参,函数体为空)
2、默认析构函数(无参,函数体为空)
3、默认拷贝构造函数,对属性进行值拷贝
构造函数调用规则如下
如果用户定义有参构造函数 c++不再提供默认无参构造 但是会提供默认拷贝构造
如果用户定义拷贝构造函数 c++不会再提供其他构造函数
//初始化列表初始化属性
person13(int a,int b, int c) :m_a(a), m_b(b), m_c(c) {}
person13 p(3,2,1);
//当其它类的对象作为本类成员,构造时先构造类对象,再构造自身,析构的顺序与构造相反
//静态成员函数
//所有对象共享同一个函数
//静态成员函数只能访问静态成员变量
//静态函数中 非静态变量 不可以访问 无法区分到底是哪个对象的m_b
//类外访问不到私有静态成员函数
//成员变量和成员函数 分开存储
非静态成员变量 属于类的对象上的
静态成员变量 不属于类对象上
非静态成员函数 不属于类对象上
静态成员函数 不属于类对象上
//空对象占用的内存空间为:1
//c++编译器会给每个空对象也分配一个字节空间,是为了区分空对象占内存的位置
//每个空对象也应该有一个独一无二的内存地址
this指针概念
this指针指向被调用的成员函数所属的对象
this指针是隐含没一个非静态成员函数内的一种指针
this指针不需要定义,直接使用即可
this指针的用途
当形参和成员变量同名时,可用this指针来区分 //解决名称冲突
在类的非静态成员函数中返回对象本身,可使用return *this //返回对象本身用*this
const修饰成员函数
常函数:
成员函数加const后我们成为这个函数为常函数
常函数内不可以修改成员属性
成员属性声明时加关键字mutable后,在常函数中依然可以修改
常对象
声明对象前加const成该对象为常对象
常对象只能调用常函数
友元
友元的目的就是让一个函数或者类 访问另一个类中的私有成员
全局函数做友元
类做友元
成员函数做友元
运算符重载
概念:对已有的运算符重新进行定义,赋予另一种功能,以适应不同的数据类型
//函数调用运算()也可以重载
//由于重载后使用的方式非常想函数额调用,因为称为仿函数
//仿函数没有固定写法,非常灵活
继承
继承是面向对象三大特性之一
继承的好处:
减少重复代码
语法:class子类:继承方式 父类
子类 也成为 派生类
父类 也成为 基类
继承方式:
公共继承
保护继承
私有继承
//父类中所有非静态成员属性都会被子类继承下去
//父类中私有成员属性 是被编译器给隐藏了 因此是访问不到 但是确实是被继承下去了
vs2017开发人员命令工具
利用开发人员命令提示工具查看对象模型
转盘盘符 D:
跳转文件路径 cd 具体路径
查看命名
cl /d1 reportSingleClassLatout类名 文件名
继承中的构造和析构顺序如下
//先构造父类再构造子类 析构的顺序和构造的顺序相反
//继承同名成员处理方式
如果子类中出现和父类同名的成员函数,子类的同名成员会隐藏掉父类中所有同名成员函数
//1、子类对象可以直接访问到子类中同名成员
//2、子类对象家作用域可以访问到父类同名成员
//3、当子类与父类拥有同名的成员函数 子类会隐藏父类中同名成员函数 如作用域可以访问到父类中同名函数
菱形继承
概念:
两个派生类继承同一个基类
又有某个类同时继承着两个派生类
这种继承被称为菱形继承,或者钻石继承
菱形继承带来的主要问题是自类继承两份相同的数据,导致资源浪费以及毫无意义,利用虚继承可以解决菱形继承问题
继承之前 加上关键字 virtual变为虚继承
多态
多态分两类
静态多态:函数重载和运算符重载属于静态多态,复用函数名
动态多态:派生类和虚函数实现运行时多态
静态多态和动态多态的区别:
静态多态的函数地址早绑定-编译阶段确定函数地址
动态多态的函数地址晚绑定-运行阶段确定函数地址
虚函数 virtual
如果想执行让猫说话,那么这个函数地址就不能提前绑定,需要在运行阶段进行绑定,也就是地址晚绑定
动态多态满足条件
1、有继承关系
2、子类需要重写父类的虚函数
动态多态的使用
1、父类的执行或者引用 执行子类对象
vfptr -虚函数(表)指针
v - virtual
f - function
ptr - pointer
vftable - 虚函数表
v - virtual
f - function
table - table
当自雷重写父类的虚函数
子类中的虚函数表 内部 会替换成 自雷的虚函数地址
当父类的指针或者引用指向子类对象时候,发生多态
//如果想扩展新的功能,需要修改源码
//在真实开发中 提倡开闭原则
//开闭原则:对扩展进行开发,对修改进行关闭
多态带来的好处
组织结构清晰
可读性强
对于前期和后期的扩展以及维护性高
纯虚函数和抽象类
在多态中,通常父类中虚函数的实现是毫无意义的,主要都是调用子类函数重写的内容因此可以将虚函数改为
纯虚函数和抽象类
纯虚函数语法:virtual 返回值类型 函数名 (参数列表) = 0;
当类中有了纯虚函数,这个类也称为抽象类
抽象类特点:
无法实例化对象
子类必须重写对象中的纯虚函数。否则也属于抽象类
虚析构和纯虚析构
多态使用时,如果子类中有属性开辟到堆区,那么父类指针在释放时无法调用到子类的析构代码
解决方式:将父类中的析构函数改为虚析构或者纯虚析构
虚析构和纯虚析构共性:
可以解决父类指针释放子类对象
都需要有具体的函数实现
虚析构和纯虚析构区别:
如果是纯虚析构,该类属于抽象类,无法实例化对象
虚析构语法
Victor ~类名(){}
纯虚析构语法
Victor ~类名()=0;
总结:
1、虚析构或纯虚析构就是用来解决通过父类指针释放子类对象
2、如果子类中没有堆区数据,可以不写位虚析构或纯虚析构
3、拥有纯虚析构函数的累也属于抽象类
文件
写文件
文件操作必须包含文件fstream
读文件可以利用ofstream,或者fstream类
打开文件时需要指定操作文件的路径,以及打开方式
利用<<可以向文件中写数据
操作完毕,要关闭文件