C++内容补充

这里写目录标题

补充本

其他

2\在函数之外 只能写变量的定义(包括定义时的初始化)以及函数的声明,具体的执行语句只能在函数体之内进行
如下是犯的一个很蠢的错误
在这里插入图片描述
1、数组名是个指针 但是 数组名是一个常量指针 不可以进行++操作或者+1等等改变值的操作 但是普通的指针变量可以

语法

函数的存储类

函数的存储类分两种,它们分别是 外部 函数和内部函数,其中 内部 函数的存储类说明不可省略,该说明符是 static 。

根据函数能否被其他源文件调用,分为内部函数和外部函数
外部函数,可以被其他源文件调用的函数
内部函数,只在定义的文件中有效

外部函数:
在一个源文件(A)定义好之后 在另一个源文件(B)调用之前,要在该源文件(B)中声明该函数,声明时加“extern”前缀,之后就可以在该文件(B)中使用之前文件定义好的函数了
定义一个函数 默认是外部函数 可以被其他源文件调用(但是要在其他文件中声明该函数,声明时也可以不加extern)

如下:
第一个源文件
int add(int x,int y)
{
return x+y;
}
第二个源文件
#include <stdio.h>
extern int add(int x,int y);
void main()
{
printf(“sum=%d\n”,add(1,2));
}

如下:

第一个源文件//第一个文件中不用声明命名空间以及iostream,因为该文件只是存个函数,不会进行输入输出
int add(int x,int y)
{
return x+y;
}
第二个源文件
#include <iostream>
using namespace std;

extern int add(int x,int y);//extern可以省略
void main()
{        
printf("sum=%d\n",add(1,2));
}

静态函数:
对于静态函数,在函数定义语句之前加上“static” 就变成了内部函数
该函数只在当前源文件有效,其他源文件无法感知到,并且由于是静态类,他的作用域是从他定义语句开始往下有效,直到当前的源文件末尾,他的寿命是等待程序结束,他才会消失

外部函数,只要声明一个函数原型,就可以调用其他源文件中的函数,但是,当多人开发时,可能出现函数重名的情况,不同源文件中的同名函数会相互干扰
此时,就需要一些特殊函数,只在定义的文件中有效,这类函数称为内部函数

代码示例:

第一个文件
#include <iostream>
using namespace std;
void show()
{
printf("%s \n","first.c");
}
第二个文件
#include <iostream>
using namespace std;
static void show()
{
printf("%s \n","second.c");
}
void main()
{        
show();
}

输出结果:second.c

第二个文件中的静态函数独立于该文件,并且与其他文件不会冲突,允许该静态函数与其他函数重名,不会起冲突

函数参数默认值

格式

即设置形参时设置默认值 ,如int add(int a,int b=1)
上面的a就是默认参数

注意:
当函数有声明时,默认值的设置要放在声明中,函数定义时就不用设置了
同时 设置默认值时 可以用常量,也可以用含义变量的表达式,如:int fun(int x , int y=a+b);(其中,a和b是已定义过具有有效值的变量)

默认参数位置

!如果一个函数中有默认参数 ,那么默认参数都在最右侧,或者说,默认参数的右边都是默认参数,不能出现没有默认值的参数
原因:如果默认参数在中间 ,那么会出现二义性
出现二义性错误的原因是因为在C++中,函数调用时,如果省略某些参数,则编译器会按照参数的位置进行匹配,但是如果出现参数缺失的情况,编译器无法确定这个参数是应该被赋予默认值还是作为后续的参数传递,从而导致二义性错误。

    	举个例子,假设有如下函数定义:

void foo(int a, int b = 1, int c = 2);
当调用该函数时,可以使用以下两种方法:

foo(10, 20); // a=10, b=20, c=2
foo(10); // a=10, b=1, c=2
在第二种调用方式中,省略了参数 b 和 c,此时编译器会自动使用参数的默认值,即 b=1 和 c=2。

    然而,如果出现以下调用方式:

foo(10, , 30); // 编译错误:二义性
这里省略了参数 b,并且给参数 c 赋值了 30,但是对于缺失的参数 b,编译器无法确定它应该被赋予默认值还是作为后续的参数传递,从而导致了二义性错误。

重载函数的默认参数

对重载函数设置默认参数值,对重载函数的选择会有影响

在这里插入图片描述
如上图所示:
当重载函数add 其中一个有默认参数 一个没有
但是如果除去默认参数 两个重载的参数列表相同,那么就会出现二义性
这时,就算你传入一个参数,他既可以调用第一个add 也可以调用第二个add 就出现了二义性
所以要尽量避免这种重载
在这里插入图片描述

指针名与正常指针的自增自减以及解引用与++的优先级问题

在这里插入图片描述

数组名是数组的指针 同时也是数组首元素的指针 他是一个指针常量
对于自增自减
数组名不可以使用 因为他是常量 但是可以+1或者-1是允许的
将数组名赋值给一个指针变量后 该指针属于普通指针 可以自增自减 也可以+1-1
对于解引用与自增自减的优先级问题
p++ 是先++后解引用 但是由于是p++,所以p++整体的值仍然是p加之前的值
对于
p+1 是先解引用后+1(*num+1也一样)

指针的赋值、加减数字、加减指针

指针的赋值:给指针赋值时一定要类型相同,级别一致
一个指针加减一个数字 表示该指针移动几位
一个指针不能加指针
一个指针减一个指针 表示两个指针之间有几个元素

指针可以比较
在指向同一个数组中两个元素的两个指针可以比较 结果是指针的位置前后
同一个位置:==
不同位置:> <
指向同一个对象的指针也可以比较 结果是相等

二维数组中的一些指针辨析+输出调用字符指针时 会将该指针以及之后的元素全部输出

在这里插入图片描述
一个字符串占一行
第一维大小指的是列数

以下以int a[3][3]为例
1、在二维数组中 a[3]只有一个中括号 代表第一个中括号 这是一个指针 指向第四行的指针,在此基础上加1 是指针在第四行向右移一位(这是一级指针)
2、而图中c选项:数组名+3 表示的是第四行的首元素的指针(例如 a+3) 对其加3是 指针向下一行移动 (但是是二级指针)

3、二维数组的数组名实际上是第一行数组的地址的指针 (二级指针),于是有如下关系:
*数组名 等价于 数组名[0] (&数组名[0])等价于数组名
*a a[0] &a[0] a

当有两个中括号时 才是数组名正常中括号取元素

二维数组未完全初始化时 某些特殊位置的值

在这里插入图片描述

在这里插入图片描述
会严格按照初始化的位置进行初始化 严格按照该在的位置进行操作 不会平铺

取模运算的注意点

1.不要对浮点数使用该运算符,那将是无效的。

2.如果第一个操作数为负数,那么得到的模也为负数;如果第一个操作数为正数,那么得到的模也为正数。

3.取模公式:a % b == a - (a/b) * b

关于指针的类型的理解

指针也是一个系统内置数据类型 将其视为一种数据类型即可

任何一个指针变量 都是用来存地址的 具体存的数据类型的指针被分为了不同类型的指针
不同类型的指针负责存不同类型数据的地址信息

包括 指向整型一维数组的指针与指向整型变量的指针 是不一样的
一个是int型指针 一级指针
一个是数组指针 二级指针

三元运算符比 == 运算优先级更低

在这里插入图片描述
例如:a==b ? c:d;

进制表示的前缀和后缀

在这里插入图片描述

for循环中的break和continue

在for循环中一旦break被触发后 那么就会跳出当前for循环
在这里插入图片描述
在for循环中一旦触发continue 那么还是会在for循环中 会继续下一个i++ 而不会跳出for循环
在这里插入图片描述

默认参数

在这里插入图片描述
函数参数列表规定了某个参数的值的话 在函数调用时 可以不传这个位置的参数 该参数有一个默认值

new动态规划数组初始化

在这里插入图片描述

直接在后面进行大括号初始化即可

用一个new动态创建一个数组时,后续更新大小

这种定义方式可根据变量 n 动态申请内存,不会出现存储空间浪费的问题。但是,如果程序执行过程中出现空间不足的情况时,则需要加大存储空间,此时需要进行如下操作:
1、新申请一个较大的内存空间,即执行int * temp = new int[m];
2、将原内存空间的数据全部复制到新申请的内存空间中,即执行memecpy(temp, p, sizeof(int)*n);
3、将原来的堆空间释放,即执行delete [] p; p = temp;

关系运算符\赋值符

== 与 =

如果想在一个循环里选出一些特别的值 那么
不可以单纯加一个if(){}
还要加else

不然运行完if后 还会去运行if下面的语句

宏定义

在这里插入图片描述
宏定义最后端不可以加分号

引用和new

在这里插入图片描述

在这里插入图片描述
在重构赋值运算符时 重构函数体内要进行两个对象属性值的赋值 (将b对象的属性值赋值给a对象)

对于第一行 是默认调用编译器提供的浅拷贝 注意第一张图 Person类的age是指针类型变量 所以p.age是指针变量,而this->age也是指针类型变量 所以编译通过,指针赋值指针;

对于第二行 我们进行深拷贝 也就是将要赋值的值在堆区开辟空间 并把数据放进去,用到new
new的语法: new 数据类型(初始值)
所以括号里应该传入一个实体值 而p.age 是类Person的属性值 通过类的定义可以看到 p.age是一个指针 所以不能直接传入 而是应该解引用 从而是一个实体值;

注意 参数是Person &p 这里将p视为一个变量 一个Person类型的变量 也就是类的对象 p是实参对象的别名 并不是地址 只不过p和实参对象共用一块地址

返回值是Person& 那么return的数据(return的值会替代函数名 也就是函数调用时 函数名最终就是return后面的数据) 必须是Person的实体变量 /因为引用类型的变量定义是 数据类型& 别名=变量名/ 因为函数类型(也就是返回值类型)是引用类型 而引用类型本身就是一个实体变量 是一个实体变量的别名 可以当做实体变量来看待 这里将引用作为返回值 是想返回该实体变量 从而该函数就会返回一个实体变量 从而后续可以将函数调用这个句子当做变量来用 可以实现链式编程

当形参是引用类型 实参可以传入实体变量 也可以传入一个地址 都可以

引用类型 实际上是实体类型 而不是指针类型 例如 int&a=b;
a的数据类型与b一致

总结引用

引用做函数参数

1、引用做函数参数 实际上就是变量的别名 传入一个实参 而形参是引用类型 那么在函数体内外 就建立了实时联系
2、因为引用类型与外面的实参有实时联系 所以在数据结构里 常常认为引用类型的功能也有返回的作用

引用做函数返回值

1、首先要注意 引用做函数返回值时 return 必须返回一个实体变量 (无论基本数据类型的变量 还是自定义类型变量) 因为函数类型就是引用 而引用类型实际上就是实体变量类型
2、引用做函数返回值 主要的功能或者想要实现的是 函数调用后 直接返回一个实体变量 而不是值 这样可以对函数进行一些操作 例如++,–。例如 int & test()++;或者直接用一个变量接住也可以 例如 int b = int &test();
3、注意 一旦引用做函数返回值 那么return的变量必须是一个长寿命变量 也就是外部的(在堆区创建)或者静态的

或者实现链式编程 因为链式编程就是对象不停的调用 并返回对象 供下一次调用

引用和指针

引用可以引指针
例如

int *p;//p是一个指针
int *& rp= p;//rp是指针p的一个引用 也是他的一个别名 rp实质上也是一个指针
int a=10;
rp=&a;//rp可以正常拿到int数据类型的地址

这时 p和rp是一个作用 且值都是10;

new二维数组

方式1

在这里插入图片描述
!! 记得释放之后 把指针指向NULL
定义一个指针类型为基本元素的数组,再对每个元素分别都new一个数组并且用数组的元素名接住

注意 最后析构的时候 也要把每个数组名的数组都析构掉 并且析构掉大数组

(可以参照new一维数组的方法来对照着记二维数组)

new后面是每个元素的数据类型 当数据类型加了一个星星的时候 每个元素的数据类型就变成了指针 这样就可以对每个元素再次进行new了
当看到后面加了星星 前面补一个*就可以

方式1的初始化

在这里插入图片描述
直接在对行进行new的时候 最后加上一个中括号 可以啥也不写 或者填个0 就可以把数组整个初始化为0

方式2(推荐)必须指定出列数

在这里插入图片描述
一个指针数组 来new出来一个二维数组
new出来之后 就可以直接使用

补充:数组指针 也就是指向数组的指针

数组指针就是定义一个指针 他指向一个数组
格式: 数据类型 + (*指针名) 【所指向的数组的元素个数】
例如 int (*p) 【3】
意为一个指针p 指向一个具有三个元素的数组 同时p也被称为二级指针

通常使用数组指针来指向二维数组的某一行,于是可以用该指针来表示二维数组

代码示例:

int a[2][3] ={4,5,6,7,8,9};
int (*p)[3]=a;

for(int i=0;i<2;i++){
	for(int j=0;j<3;j++){
		cout<<"p[i][j]"<<endl;
	}
}

p指向二维数组的首元素 那么就可以用p来表示二维数组了

也可以理解为p指向了二维数组的第一行 但是指针可以递增 所以p可以++ 意为移到第二行 依次类推 p可以指向二维数组的任意一个元素

struct与typedef

在这里插入图片描述

struct是结构体类型

typedef是给类型起别名
对于第一个:数据类型是struct{int val;} 这整个(包括花括号里的内容一起)是一个数据类型的声明(也称为匿名结构体),后面跟了A,那么A就是该结构体类型的变量

对于第二个:结构体类型声明有了名称:A 而B是A结构体的变量

第三个:struct{int val;} 这整个(包括花括号里的内容一起)是一个数据类型的声明(也称为匿名结构体),
先是一个匿名结构体 之后用typedef给匿名结构体起别名 所以 A就是匿名结构体的别名 A这次不再是变量 而是结构体的名称 是数据类型的声明

第四个:对结构体C 起别名
第一个别名是A:所以A也是结构体类型的声明 而不是结构体变量
第二个别名是B 这里的B等价于C 但是可以利用B进行指针的操作
因为C是结构体类型 所以 等价于 C 星 变量
星B 变量
!B是指向结构体的指针!

同步与异步

在这里插入图片描述

sleep()

在这里插入图片描述

时间戳(计时)

在这里插入图片描述
clock_t是一种类型 它定义的变量可以存储时间瞬时值

clock()是一个函数 可以返回当前的时间戳 也就是当前时间距离1970年1月1日0.0.0的时间差

这两个配合 可以进行计时操作

在这里插入图片描述
补充:可以进行游戏帧率的设置
在这里插入图片描述

枚举

在这里插入图片描述

结构体运算符重载

在这里插入图片描述
在这里插入图片描述

一共四种方法 第一种方法演示:
在这里插入图片描述

数组当做形参传递到函数里之后 就不可以进行数组元素的计算了

在这里插入图片描述
sizeof(nums)/sizeof(nums【0】) 可以算出数组的元素个数
但是仅限于在定义数组的函数内才可以

如果将数组作为形参 传递到函数里 那么就不可以用这种方法 因为一旦当做形参传递数组 数组就变成了指针 就不会传入整个数组的大小 所以不可以在函数里求函数外的数组的元素个数 只能在函数外求出 然后当做形参传递进去

常量表达式与变量的区别

顾名思义
在这里插入图片描述

esay x

其他

在这里插入图片描述

地图打印(利用二维数组)

在这里插入图片描述

在这里插入图片描述

双缓冲

当我们绘制一张图 然后另一张图盖住前一张图的某个部分的时候 由于while的存在 会导致 两张图不停的闪烁 所以加入双缓冲可以解决这个问题
在这里插入图片描述
开启双缓冲 之后等待Flush或者End 才会进行图片的绘制 不然不会进行图片的绘制,这样就可以保证每次while的执行 都是以一整套为单位进行的 执行到双缓冲状态结束或者flush 才将之前的内容同时绘制 也就是蓝色和白色同时绘制(这样虽然每次都在循环绘制 但是每一次绘制的效果一样 就不会出现闪烁) 而不是蓝色先绘制 之后白色再绘制

有以下两种方式
在这里插入图片描述
在这里插入图片描述
注意::
在这里插入图片描述
如果开启双缓冲状态之后 中途进行了函数的跳转 那么函数里要将双缓冲状态结束 不然一直没有结束双缓冲状态的话 函数里的绘图也不会进行绘制 而是等待双缓冲状态的结束

颜色和格式的设置

在这里插入图片描述
在这里插入图片描述
设置颜色和格式都是设置了一种状态 在这之后的填充都会采用这种状态 所以设置状态要在真正填充之前 所以每次填充之前最好都设置一次状态

键盘的输入

在这里插入图片描述
_kbhit() 以及_getch()都是控制台函数 会导致操作与界面割裂开 所以建议使用Get这个函数 该函数直接传入一个大写w即可包含小写w,因为他的参数是虚拟键
在这里插入图片描述

补充:VC虚拟值表 可放入GetAsyncKeyState的参数

https://blog.csdn.net/qq_42372031/article/details/105178789

界面的设计

在这里插入图片描述

按钮和鼠标

按钮

在这里插入图片描述
设置文本所占矩形的位置坐标 对于横轴 先将文本矩形的左边像素坐标移动到按钮的中间 之后 减去文本矩形像素的一半 就可以实现左边一半右边一半 就可以对齐了
纵轴同理
在这里插入图片描述

鼠标

在这里插入图片描述
先对鼠标的状态进行设置,采用枚举类型

同时定义全局变量 Message msg; 或者 ExMessage msg;

在这里插入图片描述
定义鼠标状态函数 获取在某一时刻 各个按钮内的鼠标状态

之后 获取到鼠标的位置 是否在按钮中间 并对鼠标是否点击进行判断 并返回枚举变量

鼠标状态判断函数的参数是按钮的结构体变量,也就是对每个按钮分别判断 以按钮为视角 看鼠标是否在该按钮内 一个按钮一个按钮的方式进行判断

在这里插入图片描述
参数分别是三个按钮 返回值分别是在目前的时刻 鼠标在三个按钮的状态
在绘图函数里调用判断鼠标状态
if(peekmessage(&msg,EX_MOUSE))用来判断鼠标是否点击在当前程序 有可能鼠标在该程序以外的地方操作

鼠标经过时变色

在这里插入图片描述
首先对按钮的绘制函数进行颜色的参数设置,方便后续更改 注意 颜色宏定义的类型是int类型

在这里插入图片描述
注意画按钮的函数 参数先添加一个颜色变量

之后 函数体里填充按钮颜色的参数也要改为buttoncolor 不可以再使用自己规定死的颜色了

在这里插入图片描述
之后 将状态整数放在while外面 使其作用域包含整个while

注意点:只要画面要进行动态变化的 都要放入循环里 所以 按钮绘图也要放入循环
然后用三目运算符进行传参数

查阅帮助文档

(以textwidth为例)

在这里插入图片描述
注意 要严格按照文档参数类型的规定 去写代码
如上图 规定参数是LPCTSTR类型的字符串 那么在程序中就要这样定义
在这里插入图片描述

将某段文字放在某个位置

在这里插入图片描述
用到outtextxy函数 其中参数分别为 第一个字母的x坐标 第一个字母的y坐标 文本

如下图 注意 必须是LPCTSTR类型的字符串
在这里插入图片描述

类和对象

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

在这里插入图片描述
只有一个类壳子的时候 类和对象的大小都是1
在这里插入图片描述
当类中只有一个int数据的时候
类和对象的大小都是4
在这里插入图片描述
当类中有int数据以及一个静态变量的时候
类和对象的大小还是4

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

在这里插入图片描述
在这里插入图片描述
所有的局部变量默认存储类是auto类
对象的存储类跟变量一致 因为对象也是一个变量 是自定义类的变量

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

文件操作

键盘输入
数据流

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

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

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

cin>>

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

get()

在这里插入图片描述
在这里插入图片描述
因为cin是默认键盘输入对象 所以要用cin调用get函数 返回所获取的字符 文件结束时返回EOF

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

geiline()

在这里插入图片描述
返回值是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的用法:
在这里插入图片描述

在这里插入图片描述
cin的gcount函数
返回值是int 无参数

read()

在这里插入图片描述
不会只读取一行 而是会读取全部 ctrl+z之前的都被读取

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

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

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

构造时可以利用初始化成员列表 (前提是父类以及成员对象的类 都有有参构造)
在这里插入图片描述

在这里插入图片描述
继承后 想要通过子类对象对继承而来的父类成员进行赋值 可以通过初始化列表 对父类初始化时 是用类名+括号
对于子对象成员 (即自己某个成员是一个对象)也可以放在初始化成员列表进行初始化 这里是用对象名+括号

同时对于初始化成员列表 还可以对一个类的常量进行初始化(const 修饰的变量)如下图
在这里插入图片描述
当然直接在类定义的时候给const赋值也没啥问题 但是这样的话const变量在类定义好之后就无法改变 在开发过程中会有些不方便
在这里插入图片描述

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

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

当有有参和无参之后 也可以直接在子类构造函数里调用父类有参构造函数
在这里插入图片描述

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

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

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

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

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

在这里插入图片描述
对数组赋值 通过构造函数得到数组名 也就是数组的地址 之后for循环依次赋值 不能直接数组名=数组名
当求加和时 一定要把初始值初始化为0 不然初始值就是他的地址 出错

对象生存期

在这里插入图片描述

静态数据成员(static)

在这里插入图片描述
在这里插入图片描述
对于静态成员属性 首先他是类共有的 每个该类的对象 都在共享静态成员数据 并且实时共享 也就是一个对象进行操作之后 另一个对象调用时得到的结构是更新之后的结构
静态成员必须在类内声明 ,类外初始化
在main函数中 静态成员属性除了可以用对象名引用之外 还可以用类名引用
在这里插入图片描述
在这里插入图片描述
静态成员函数是整个类的 各个对象共享 每个对象都能调用他
静态成员函数可以在类内也可以在类外进行实现
在main函数中 可以直接类名+::+函数名 进行成员函数的调用 也可以用常规的对象.函数名

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

常成员(const)

在这里插入图片描述
在这里插入图片描述
加了const 也就是只能读 不能改 变为常量 上面是其初始化的格式 当然 直接在类内定义的时候初始化也可以
如下:
在这里插入图片描述

调用无参构造

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

只有成员函数的类的大小

在这里插入图片描述
成员函数不占用类的空间 而一个空类的大小是一个字节 因为要用一个字节用来占位

匿名对象

在这里插入图片描述

正常对象:
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对象地址的时候 会调用该对象类的析构函数

释放堆区数据的实质

在这里插入图片描述
如上图 当创建完Employee对象之后 释放了worker
紧接着下一行 仍然可以使用worker指针

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

自己是自己的友元

在这里插入图片描述
在类中的成员函数中
如果某些成员函数的参数是自己类的某个对象 那么在该函数体里 可以调用该参数对象的私有成员

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

职工管理系统

分区编写代码
格式

1、 将类的定义以及类的属性声明写在头文件里
可以视为类的框架
在这里插入图片描述
同时在源文件写头文件中类的声明的具体实现 直接包含对应的头文件 注意写实现时要写明作用域也就是类名 类实现的源文件服务于类声明的头文件 但是主要的操作内容要在该源文件实现
在这里插入图片描述

3、在程序的入口源文件 也就是包含main函数的文件里 (首先要明确 所有的源文件是独立的 不会直接共享 同时头文件与源文件独立)
想要访问头文件以及与头文件相辅相成的具体实现文件 就要包含头文件(注意 具体实现文件也要包含对应的头文件才能建立联系)
在这里插入图片描述

进一步理解

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

while(1)以及Switch

在这里插入图片描述
1、while(1)的作用是 卡住页面不动 也就是用户没有输入之前 保持住页面的展示 换句话说是提供监听功能

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

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

判断文本文件是否为空

在这里插入图片描述
首先读出一个字符 然后调用输入类的eof函数 该函数可以查看该字符是不是文件结束标志 也就是该函数返回值如果为真 那么就是读到了结尾

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

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

在这里插入图片描述
上图中 不采用任何API函数 而是直接利用ifs对象进行读数据 那么会以空格为分隔符 一个一个数据进行读取

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

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

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

在这里插入图片描述
1、如上图for循环的内容 首先 i 定位在要删除的索引位置并且 i<num-1 (实际上i的范围就是要操作的索引范围 但是因为每次操作涉及到两个紧挨着的数据 且i是第一个数据的索引 所以 最终 i 的截止位置是倒数第二个元素的索引 所以小于num是最后一个元素 小于num-1就是倒数第二个元素)

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

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

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

在这里插入图片描述
如上图 可以直接delete数组中某个元素的地址 删除之后 该地址仍然可以使用 因为delete删除的是该地址的数据 地址仍然可以使用

释放时要释放干净

在这里插入图片描述
要依次释放在堆区申请的空间 利用for循环

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

机房预约系统

关于继承和多态的理解

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

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

纯虚函数的实现

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

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

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

宏的文件

在这里插入图片描述
可以创建一个头文件 globalFile.h头文件

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

文件名传入函数参数

定义文件名为string即可

return or return 0

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

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

在这里插入图片描述
如图为上面三点的代码

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

当子类构造继承得来的属性时 父类的无参构造必须有 无论是系统的还是后续补上的 总之父类必须有默认构造函数 或者说 无参构造函数 这样才能在子类的构造函数里利用this来初始化从父类继承得到的属性
在这里插入图片描述
在这里插入图片描述

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

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

之后 就是弥补了“用匿名对象实现多态时 无法用子类对象来调用自己独有功能”的弊端
将父类指针转为子类指针(当然 子类指针也可以转为父类指针)
转为子类指针之后 就可以通过该指针来调用子类的自己的功能和属性了
在这里插入图片描述
接口调用时 参数是父类指针 要传入一个已经实现了多态的父类指针

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

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

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

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

在这里插入图片描述
图中的ifs>>fid 以及 ifs>>fname 等等操作

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

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

在这里插入图片描述

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

(补充 ofs<<endl 会将回车写入文件 如上图 左移什么,就会写入什么)
在这里插入图片描述
注意 可以使用追加的方式写文件 如上图格式 有需求时可以采用

清空文件

在这里插入图片描述
trunc的打开方式是 如果文件存在 给他删干净 再重新创建

注销的操作

在这里插入图片描述
因为每次登录 都是拿着一个对象去操作或者说创建一个对象 利用多态来操作
当注销时 该对象就可以被释放了 所以注销就是释放new出来的对象 释放他的父类指针即可 (因为他的地址在父类指针中存放着)而函数传参时父类指针类型的变量是用引用的 所以student与父类指针person变量在内存中是同一块地址
所以 delete student即可

输入的设计

在这里插入图片描述
对于一些输入 要用死循环来处理 因为用户可能会输入有误 当输入有误时要重新输入 所以 如果一直有误 那么要一直重新输入 所以用一个死循环处理

map<int ,map<>>

在这里插入图片描述
小map做为大map其中元素的一部分

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

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

调用:
在这里插入图片描述

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

在这里插入图片描述
ifs与cin读取是一致的 会自动跳过空格读取 所以可以直接ifs>>变量 无需考虑空格

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

字符串截取

在这里插入图片描述
字符串截取

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

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

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

在这里插入图片描述
在这里插入图片描述

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

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

字符串拼接

在这里插入图片描述

三元运算符

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

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

在这里插入图片描述
如上图 我们要取出第1、2、4条记录 并且要拿到他们在原容器中的位置 到时候要通过这些位置从容器中取出

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

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

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值