C++内容补充--面向对象篇

类和对象

类中静态成员不占用类的大小 也就不占用类的对象的大小

C++内容补充--面向对象篇_第1张图片
只有一个类壳子的时候 类和对象的大小都是1
C++内容补充--面向对象篇_第2张图片
当类中只有一个int数据的时候
类和对象的大小都是4
C++内容补充--面向对象篇_第3张图片
当类中有int数据以及一个静态变量的时候
类和对象的大小还是4

变量存储类以及对象的存储类

C++内容补充--面向对象篇_第4张图片
C++内容补充--面向对象篇_第5张图片
所有的局部变量默认存储类是auto类
对象的存储类跟变量一致 因为对象也是一个变量 是自定义类的变量

存储类为外部和静态的数组其元素具有默认值。

文件操作

键盘输入

数据流

中心思想:键盘输入的东西 可以可以看做一个文件 称为“键盘输入文件”

首先我们要明确 从键盘上输入数据是带缓冲区的,只有输入完数据,并且在数据输入完之后按回车,就会形成输入流,也就是完成了文件到输入流的转化,就可以向程序中输入数据了(注意 如果数据没有输入完 也就是还有变量没对应数据 那么这时的回车是被当做空格符的)

键盘设备的输入流对象系统规定为cin,所以cin是一个系统默认键盘输入流对象,而ifstream ifs 是一个文件输入流对象,与cin的区别是cin是键盘文件的输入流对象 ,ifs是某个内存文件的输入流对象

cin>>

C++内容补充--面向对象篇_第6张图片
首先将输入输入到缓冲区 形成键盘文件 敲下回车之后 形成输入流 那么cin>>就可以从输入流中获取数据赋值给后面的变量
在这里插入图片描述
如果提取失败 那么cin的值就是0
cin>>x 表达式的值也是0 所以可以将其作为条件判断接下来是否还有键盘输入(常用于算法程序 判断还会不会给程序塞数据,因为算法程序的测试用例,就可以看成一个已经形成好的键盘文件,并且形成了输入流,拿着这个输入流塞给程序)

get()

C++内容补充--面向对象篇_第7张图片
C++内容补充--面向对象篇_第8张图片
因为cin是默认键盘输入对象 所以要用cin调用get函数 返回所获取的字符 文件结束时返回EOF

输入数据 按下回车键之后 形成输入流 之后交给get函数处理 get函数将字符一个一个读入程序,当读完时,意味着文件结束(这里的文件是键盘输入文件),那么get就会读到EOF(也就是-1)所以可以使用该条件来判断读入是否结束

geiline()

C++内容补充--面向对象篇_第9张图片
返回值是void 参数是存放的字符数组的数组名或地址,最大读取字符个数限制n 一般为字符数组的大小 ,因为每次都是往那个字符数组中存放

上图中 每次回车都是一个输入流 那么他的程序结束是用ctrl+z来表示接下来没有输入了,使得程序结束
(ps:对于算法题来说 不用ctrl+z 因为是系统强制输入数据 算法测试用例已经是一个键盘文件的输入流 所以直接将该输入流通过getline解析,之后判断目标字符数组str中的字符个数是否为0 就能告诉程序测试用例输入了几行)

getline()用法
getline是C++标准库函数;它有两种形式,一种是头文件< istream >中输入流成员函数;一种在头文件< string >中普通函数;

它遇到以下情况发生会导致生成的本字符串结束:
(1)到文件结束,(2)遇到函数的定界符,(3)输入达到最大限度 n。

输入流成员函数getline()
函数语法结构:

在< istream >中的getline()函数有两种重载形式:

istream& getline (char* s, streamsize n );(默认截止字符是‘\0’)
istream& getline (char* s, streamsize n, char delim);

作用是: 从istream中读取至多n个字符(包含结束标记符)保存在s对应的数组中。即使还没读够n个字符,
如果遇到delim 或 已经将输入的字符读完了(即文件结束),则读取终止,delim都不会被保存进s对应的数组中。

代码实例

#include
using namespace std;

int main()
{
char name[256];
cout << "Please input your name: ";
cin.getline(name, 256);
cout << "The result is: " << name << endl;

return 0;

}
#include
using namespace std;

int main( )
{
char line[100];
cout << " Type a line terminated by ‘t’" << endl;
cin.getline( line, 100, ‘t’ );
cout << line << endl;

return 0;
}

string中 getline的用法:
C++内容补充--面向对象篇_第10张图片

C++内容补充--面向对象篇_第11张图片
cin的gcount函数
返回值是int 无参数

read()

C++内容补充--面向对象篇_第12张图片
不会只读取一行 而是会读取全部 ctrl+z之前的都被读取

类+子对象成员+父类继承的构造以及构造顺序

当一个类 继承了父类 同时也有一个对象作为数据属性(下面称作子对象 但是并不是继承关系 只是一个类的对象而已)
那么构造该类时 会先构造父类 之后构造子对象 最后构造自己(子对象的初始化构造只能在初始化列表中进行)

比如 一个Student 类 成员有phone类对象 继承着Person类 那么构造顺序:Person Phone Student

构造时可以利用初始化成员列表 (前提是父类以及成员对象的类 都有有参构造)
C++内容补充--面向对象篇_第13张图片

C++内容补充--面向对象篇_第14张图片
继承后 想要通过子类对象对继承而来的父类成员进行赋值 可以通过初始化列表 对父类初始化时 是用类名+括号
对于子对象成员 (即自己某个成员是一个对象)也可以放在初始化成员列表进行初始化 这里是用对象名+括号

同时对于初始化成员列表 还可以对一个类的常量进行初始化(const 修饰的变量)如下图
C++内容补充--面向对象篇_第15张图片
当然直接在类定义的时候给const赋值也没啥问题 但是这样的话const变量在类定义好之后就无法改变 在开发过程中会有些不方便
C++内容补充--面向对象篇_第16张图片

创建子类对象时 从父类继承的成员的初始化方式(第二种可能更常用 因为父类没有无参构造会有很多麻烦)

一种就是上面的利用初始化成员列表,适用于父类有 有参构造函数,直接用初始化成员列表即可(补充:子对象的初始化构造必须使用初始化成员列表)
还有一种 因为子类继承父类之后 父类的成员都归子类旗下
所以 可以直接在子类的构造函数里 对继承来的成员进行赋值 前提是父类的无参构造要存在(或者说默认构造)因为是直接对成员赋值 没有调用有参构造 所以会调用无参构造
C++内容补充--面向对象篇_第17张图片
C++内容补充--面向对象篇_第18张图片
C++内容补充--面向对象篇_第19张图片
C++内容补充--面向对象篇_第20张图片
当补上无参构造或者将有参构造删去(系统默认无参构造生效) 那么就可以直接在子类的构造函数里对继承得来的成员进行赋值,
!!!也就是将其全部视为子类属性 进行赋值 进行使用

当有有参和无参之后 也可以直接在子类构造函数里调用父类有参构造函数
C++内容补充--面向对象篇_第21张图片

对于子类调用父类方法时 会不会调用父类无参构造的问题

代码调试显示:
当子类用上述初始化成员列表来初始化父类中的成员属性(或者说从父类继承下来的成员属性)从而创建出子类对象s时,这时用此子类对象s调用继承到的父类的成员方法时,不会调用父类的构造函数
(是因为初始化成员列表对属性赋值时是使用父类的匿名对象来赋值 无需构造函数)
当子类使用上述第二种方法初始化从父类继承得到的成员时,这时创建子类对象就要使用子类的无参构造,这时子类调用从父类继承到的成员方法时,会调用父类构造函数

结论:
所以可以得知 子类对象调用从父类继承来的函数时 不会调用父类构造函数,因为构造函数只是用来构造出数据 与函数调用本身是无关联的
所以,控制台输出的调用父类无参构造,跟子类对象调用继承得来的函数没有关系,
而是,跟上面所说的子类对象的构造方法有关系:
使用初始化成员列表来初始化父类中的成员属性(或者说从父类继承下来的成员属性)从而创建出子类对象s时,不会调用父类构造函数
使用上述第二种方法初始化从父类继承得到的成员时,这时创建子类对象就要使用子类的无参构造,会调用父类构造函数

所以说 是否调用父类无参构造函数,跟子类调用继承得到的函数没关系 但是调用函数之前 创建一个子类对象是必须的 所以会有不同的构造方法有不同的输出结果

对类的数据成员属性是个数组时的构造函数 以及求加和时的注意事项

C++内容补充--面向对象篇_第22张图片
对数组赋值 通过构造函数得到数组名 也就是数组的地址 之后for循环依次赋值 不能直接数组名=数组名
当求加和时 一定要把初始值初始化为0 不然初始值就是他的地址 出错

对象生存期

C++内容补充--面向对象篇_第23张图片

静态数据成员(static)



对于静态成员属性 首先他是类共有的 每个该类的对象 都在共享静态成员数据 并且实时共享 也就是一个对象进行操作之后 另一个对象调用时得到的结构是更新之后的结构
静态成员必须在类内声明 ,类外初始化
在main函数中 静态成员属性除了可以用对象名引用之外 还可以用类名引用


静态成员函数是整个类的 各个对象共享 每个对象都能调用他
静态成员函数可以在类内也可以在类外进行实现
在main函数中 可以直接类名+::+函数名 进行成员函数的调用 也可以用常规的对象.函数名

与成员属性相比多一条是:在成员函数里 可以 类名.静态成员属性 来进行成员属性的引用 前提是在静态成员函数里 而对于非静态成员只能用this->来引用(引用的是当前对象的属性)或者对象名.属性(引用的是其他对象的属性)

常成员(const)


C++内容补充--面向对象篇_第24张图片
加了const 也就是只能读 不能改 变为常量 上面是其初始化的格式 当然 直接在类内定义的时候初始化也可以
如下:
C++内容补充--面向对象篇_第25张图片

调用无参构造

1\ 调用无参构造函数的时候 不能加括号 直接Person p; 即可

只有成员函数的类的大小

C++内容补充--面向对象篇_第26张图片
成员函数不占用类的空间 而一个空类的大小是一个字节 因为要用一个字节用来占位

匿名对象

在这里插入图片描述

正常对象:
A a(10);

匿名对象:
A(10);
直接调用构造函数 没有对象接住 那么这个整体就是匿名对象
匿名对象的生命周期:
该匿名对象使用完之后 就会被析构 也就是匿名对象所在行执行完 就会被析构

析构函数

析构函数的调用时机

1、一个类系统会提供析构函数 当一个对象执行完没用了之后 系统会自动调用该类析构函数
2、当一个类中存在new数据 那么就要重载析构函数 将new的数据释放掉 并将指针置空 这时由于new发生在类内 并且类外创建对象时没有利用new的方式创建对象(即正常的创建实体 而不是生成对象的指针),那么系统会自己调用析构
3、创建类时用new来创建对象 返回了对象指针 那么要手动delete对象指针,当delete new出来的那块内存时 会调用析构函数
这时如果类内有new 同时重载了析构函数 将类内的new都delete掉了 那么就算是完满了
如果类内没有new 那么无需其他步骤 将类外的对象指针释放掉之后 就完满了

解析函数释放谁

析构函数
首先 我们知道 对于系统内置数据类型 new出来之后 直接delete就可以了

但是对于一个对象来说 因为是通过自定义类来创建的 类中的构造函数可能会new多个地址
那么当一个对象使用完之后 就要将这些new都释放掉 那我们的析构函数 就是一个释放地址的集合函数 所以在该类中new出来的地址 都放在析构函数里进行delete 当我们在main函数delete一个对象地址的时候 会自动调用析构函数 那么就把该类的对象的new出来的地址 统一全部释放掉了

同时 一些不是本类中new出来的地址 也可以借助析构函数来进行delete 但是要谨慎使用
总之 delete对象地址的时候 会调用该对象类的析构函数

释放堆区数据的实质

C++内容补充--面向对象篇_第27张图片
如上图 当创建完Employee对象之后 释放了worker
紧接着下一行 仍然可以使用worker指针

说明 释放的是指针变量中存放的数据 而不是指针变量自己 指针变量仍然可以使用

自己是自己的友元

C++内容补充--面向对象篇_第28张图片
在类中的成员函数中
如果某些成员函数的参数是自己类的某个对象 那么在该函数体里 可以调用该参数对象的私有成员

因为自己类是自己类的友元 自己类的成员函数可以访问自己类对象的私有成员
详情参照c+±-类和对象中“类做友元”

职工管理系统

分区编写代码

格式

1、 将类的定义以及类的属性声明写在头文件里
可以视为类的框架
C++内容补充--面向对象篇_第29张图片
同时在源文件写头文件中类的声明的具体实现 直接包含对应的头文件 注意写实现时要写明作用域也就是类名 类实现的源文件服务于类声明的头文件 但是主要的操作内容要在该源文件实现
C++内容补充--面向对象篇_第30张图片

3、在程序的入口源文件 也就是包含main函数的文件里 (首先要明确 所有的源文件是独立的 不会直接共享 同时头文件与源文件独立)
想要访问头文件以及与头文件相辅相成的具体实现文件 就要包含头文件(注意 具体实现文件也要包含对应的头文件才能建立联系)
C++内容补充--面向对象篇_第31张图片

进一步理解

C++内容补充--面向对象篇_第32张图片
创建的管理类 实际上就是把之前在主文件中的所有函数以及用到的成员属性 都放在一个类里面进行管理 这样可以利用类 更加灵活 使用时直接在main函数创建管理类的对象使用即可
C++内容补充--面向对象篇_第33张图片
在管理类头文件的具体实现文件当中 如果用到了某些其他头文件里的定义 那么只需要在该文件对应的头文件里包含即可(如上上图)

while(1)以及Switch

C++内容补充--面向对象篇_第34张图片
1、while(1)的作用是 卡住页面不动 也就是用户没有输入之前 保持住页面的展示 换句话说是提供监听功能

2、Switch case语句中的break
break的作用是退出当前循环 在switch中 break 只会跳出switch框架 之后会继续向下执行switch框架之外东西

3、switch case语句中的continue
continue会跳出switch框架 并跳过Switch·之外的语句 直接进行下一次循环

判断文本文件是否为空

C++内容补充--面向对象篇_第35张图片
首先读出一个字符 然后调用输入类的eof函数 该函数可以查看该字符是不是文件结束标志 也就是该函数返回值如果为真 那么就是读到了结尾

所以如果先读一个字符就到了结尾的话 那么该文件肯定为空

文本文件以空格为分隔符进行读取数据到指定的数据变量中

C++内容补充--面向对象篇_第36张图片
上图中 不采用任何API函数 而是直接利用ifs对象进行读数据 那么会以空格为分隔符 一个一个数据进行读取

图中的 Id name dId 都是待存放读取到的数据的数据变量
右移运算符 + 数据变量 意思就是将读取到的数据放入数据变量里

补充 图中利用&& 那么条件变成 读取完这些数据之后 意为完成一个周期

删除数组中的一个数据–数组前移

C++内容补充--面向对象篇_第37张图片
1、如上图for循环的内容 首先 i 定位在要删除的索引位置并且 i

2、想要删除一个数组元素 直接将该元素后面的元素一次前移 就把该元素覆盖住了 同时因为删除该元素 该元素就是空元素 所以应用数据结构中顺序表移动的思想 要向着空元素的位置依次移动 所以是从前面开始依次移动

3、注意更新数组元素个数 同时 !! 记得更新文件的内容 就是要写入文件的函数再调用一下 将堆区数据写入文件

delete释放地址之后数组的状态

C++内容补充--面向对象篇_第38张图片
如上图 可以直接delete数组中某个元素的地址 删除之后 该地址仍然可以使用 因为delete删除的是该地址的数据 地址仍然可以使用

释放时要释放干净

C++内容补充--面向对象篇_第39张图片
要依次释放在堆区申请的空间 利用for循环

注意 释放之后 因为释放的是数据 而不是地址 所以 要把地址指向空

机房预约系统

关于继承和多态的理解

当多个派生类继承一个多态类

是因为多态类有这些派生类的所有共性 所以会抽象出来他们的共性
而对于成员属性的继承 子类会继承到父类的属性 父类的成员归子类所有
而对于共性函数的继承(注意是派生类中共性的函数) 就形成了函数重写 就形成了多态

纯虚函数的实现

首先 ,对于子类继承父类 同时重写父类中的纯虚函数时,子类中的该函数不再是纯虚函数,而是单纯的虚函数,所以子类重写父类的纯虚函数之后,就不再是抽象类了,因为子类中已经没有了纯虚函数,就可以实例化对象了

第二,如果非要对父类的纯虚函数进行实现(比如父类的纯虚析构函数的实现),就必须在类外进行实现,实现方式是

返回值 类名::函数名(){ }
或者
返回值 类名::~函数名(){ }

宏的文件

C++内容补充--面向对象篇_第40张图片
可以创建一个头文件 globalFile.h头文件

里面存放所有的宏定义 这样以后在哪个文件用到了 就直接包含这个头文件 就可以使用里面的任何一个宏定义了

文件名传入函数参数

定义文件名为string即可

return or return 0

看函数的返回值 如果是void那么退出函数可以用return;
如果返回值是int 那么可以用return 0;

多态实现时 可以先创建出父类指针 置为空

C++内容补充--面向对象篇_第41张图片
如图为上面三点的代码

字类构造继承而来的属性时的注意点

当子类构造继承得来的属性时 父类的无参构造必须有 无论是系统的还是后续补上的 总之父类必须有默认构造函数 或者说 无参构造函数 这样才能在子类的构造函数里利用this来初始化从父类继承得到的属性
C++内容补充--面向对象篇_第42张图片
C++内容补充--面向对象篇_第43张图片

弥补“用匿名对象实现多态时 无法用子类对象来调用自己独有功能”的弊端

在这里插入图片描述
图中manager是父类指针 他用于接收已经实现了多态的父类指针 所以循环中第一行代码 是父类指针调用被重写的函数

之后 就是弥补了“用匿名对象实现多态时 无法用子类对象来调用自己独有功能”的弊端
将父类指针转为子类指针(当然 子类指针也可以转为父类指针)
转为子类指针之后 就可以通过该指针来调用子类的自己的功能和属性了
C++内容补充--面向对象篇_第44张图片
接口调用时 参数是父类指针 要传入一个已经实现了多态的父类指针

对象指针做参数时,最好用引用指针

我们指定 一个对象做函数参数时 最好参数设置为引用类型 因为这样可以提高效率,同时避免浅拷贝问题
那么当一个对象指针做函数参数时 也要设置为一个指针类型的引用
格式:将指针看成一个系统类型 如上图 将Identity * 看成整体 这是一个指针类型变量 存放一个地址 然后类型+&+引用名 就是引用的定义格式 所以就是Identity 星 &manager

Identity * &manager = person
int &b=a
如上,这类比int类型的引用(Identity 星 相当于 int ) 上面manager是一个对person的引用 也就是对一个指针类型的引用 所以manager本质上还是person

ifs的本质(输入流右移运算的本质)

C++内容补充--面向对象篇_第45张图片
图中的ifs>>fid 以及 ifs>>fname 等等操作

类似于cin>>fid 只不过ifs是从内存文件读入数据,放入>>右边的变量中 而cin是从键盘输入流中读取数据
ifs>>与cin>>一样 以空格为分割 以回车形成输入流 所以当进行完一轮判断后 一行也就被读完了
而循环的作用是让他把所有行都读完 之后if判断 看哪一行与键盘输入的一致
如果一致则成功 之后创建相应的对象 建立多态 同时还要转到下一个子菜单(图中new的下一行空行就是该功能)

ofs写文件的本质(向文件写数据)

C++内容补充--面向对象篇_第46张图片

ofs是ofstream对象 是输出流对象 这里的输入输出是相对于程序exe来说的 从程序向文件写东西 这是输出流
ofs与cout如出一辙 只不过cout是从程序向键盘文件写东西 俗称输出

(补充 ofs< C++内容补充--面向对象篇_第47张图片
注意 可以使用追加的方式写文件 如上图格式 有需求时可以采用

清空文件

C++内容补充--面向对象篇_第48张图片
trunc的打开方式是 如果文件存在 给他删干净 再重新创建

注销的操作

C++内容补充--面向对象篇_第49张图片
因为每次登录 都是拿着一个对象去操作或者说创建一个对象 利用多态来操作
当注销时 该对象就可以被释放了 所以注销就是释放new出来的对象 释放他的父类指针即可 (因为他的地址在父类指针中存放着)而函数传参时父类指针类型的变量是用引用的 所以student与父类指针person变量在内存中是同一块地址
所以 delete student即可

输入的设计

C++内容补充--面向对象篇_第50张图片
对于一些输入 要用死循环来处理 因为用户可能会输入有误 当输入有误时要重新输入 所以 如果一直有误 那么要一直重新输入 所以用一个死循环处理

map>

C++内容补充--面向对象篇_第51张图片
小map做为大map其中元素的一部分

由于是小map也可以存多条序偶 所以这相当于是一个二维的

注意点 构造小map时 一定要加上map 如上图小三角

调用:
C++内容补充--面向对象篇_第52张图片

读取文件(ifs)时是否考虑空格与回车

C++内容补充--面向对象篇_第53张图片
ifs与cin读取是一致的 会自动跳过空格读取 所以可以直接ifs>>变量 无需考虑空格

同时又因为遇到回车 就形成流了 所以 最后有回车的话 也不用进行>> 所以无需对回车有啥处理 遇到回车形成流

字符串截取

C++内容补充--面向对象篇_第54张图片
字符串截取

find函数 返回值是该字符的在字符串中的数组下标
如果没找到 返回-1

之后利用数学归纳法 截取子串

string转字符 再将数字字符转为int型数字

C++内容补充--面向对象篇_第55张图片
C++内容补充--面向对象篇_第56张图片

首先通过图中通过key得到了value 是一个string类型的字符 调用string函数 c_str()函数 转为字符数组类型 然后用atoi(字符数组)再将数字字符转为int型数字

详情可查阅算法专栏“算法知识+错题本”一文中STL部分

字符串拼接

C++内容补充--面向对象篇_第57张图片

三元运算符

在这里插入图片描述
有时候较为方便

将一个序列中的某些序号保存并且可以取出

C++内容补充--面向对象篇_第58张图片
如上图 我们要取出第1、2、4条记录 并且要拿到他们在原容器中的位置 到时候要通过这些位置从容器中取出

可以利用vector容器 每次取出一条记录 就要将该记录在原数组中的数组下标存入一个新的vector容器

所以 vector中 按照容器序号0、1、2、…的顺序 依次存入原容器中的序号
这样 通过vector的key 可以拿到value(原容器中的位置)

你可能感兴趣的:(c++)