C++学习笔记
文章目录
- C++基础
- 通讯录管理系统
- C++核心编程(内存和引用)
- 函数提高
- 职工管理系统
- C++提高编程
- 演讲比赛
- 机房预约管理系统
C++基础
一,变量类型和C语言基本相同
(1)int,char,float,double,bool(布尔型只有0或1)等,各类型在C++中区别在于所占内存空间不同。
(2)C和C++中字符型(char)变量只占用1个字节。
(3)字符型(char)变量并不是把字符本身放到内存中存储,而是将对应的ASCII编码放入到存储单元。
拓展:(ASCII码大致由以下两部分组成:1. ASCII非打印控制字符: ASCII表上的数字0-31分配给了控制字符,用于控制像打印机等一 些外围设备。2 . ASCII打印字符:数字32-126分配给了能在键盘上找到的字符,当查看或打印文档时就会出现。)
二,标识符命名规则(作用: C++规定给标识符(变量、常量)命名时,有一套自己的规则。)
( )例
int c_l_a=10;//即c-const g-global l-local
string name;//即创建名字的字符串
(1)标识符不能是关键字。例(for,case,define,等)。
(2)第一个字符必须为字母或下划线。例(DO,Size,_G3 ,IF )错误例子(4d, -sub)。
(3)标识符中字母区分大小写。例(for不可以使用,For则可以)。
三,转义字符(作用:用于表示-些不能显示出来的ASCII字符)
(1)\a 含义:警报 ASCII(十进制)007
(2)\n 含义:换行(LF),将当前位置移到下一行开头 ASCII(十进制)010
(3)\t 含义:水平制表(HT) (跳到下 个TAB位置)即可以整齐输出数据 ASCII(十进制)009
(4)\ \ 含义:代表-个反斜线字符"\" ASCII(十进制)092
(5)\r 含义:回车(CR),将当前位置移到本行开头 ASCII(十进制)013
(6)\b 含义:退格(BS),将当前位置移到前一-列 ASCII(十进制)008
(7)\0 含义:数字0
四,字符串
(1) C++中字符串可以 string 变量名=“字符串值” 需要加入头文件#include
用string创建后,字符串一旦初始化就不能更改, 因为string类中所有字符串都是常量,数据是无法更改,由于string对象的不可变, 所以可以共享。对String类的任何改变,都是返回-一个新的String类对象。
(2)null空字符(\0)用他标记字符串的结束
(3)用%s来读取字符串的数据
五,运算符
1.C++中访问具体的数需要用&取址符
六,系统生成随机数方法
#include<ctime>
//time系统时间头文件包含
srand((unsingned int)time(NULL));
//随机数种子:利用当前系统时间生成随机数
int num=rend()%100+1;
//rend()%100+1生成0+1~99+1随机数。
七,跳转语句(break)
作用:用于跳出选择结构或者循环结构
break使用的时机:
1.出现在switch条件语句中, 作用是终止case并跳出switch
2.出现在循环语句中,作用是跳出当前的循环语句
3.出现在嵌套循环中,跳出最近的内层循环语句
八,数组元素互换输出
1,计算数组长度下标方法:
int end = sizeof (arr) / sizeof (arr[0]) -1
;//减1是因为sizeof (arr) / sizeof (arr [0])=5而数组下标为0 4故需减去1。
for(start = 0; start < end; start++, end-){
int temp=arr[start];
arr[start]=arr[end];
arr[end]=temp;
}
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-S7ALQkJG-1676032966222)(C:\Users\y’z\Desktop\C++\C++基础\屏幕截图 2022-11-05 105844.png)]
九,数组冒泡排序
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-HyqP6mSs-1676032966223)(C:\Users\y’z\Desktop\C++\C++基础\屏幕截图 2022-11-05 111133.png)]
十,函数
在函数定义中
声明可以写多次,但定义只能有一次。
十一,函数的值传递和地址传递
1,值传递时,形参发生任何的改变都不会影响实参。
图示
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-gFbI6vfJ-1676032966224)(C:\Users\y’z\Desktop\C++\c++核心编程\屏幕截图 2022-11-12 145443.png)]
2,地址传递时,形参发生改变可以影响实参。
例
void func(int*a,int*b)
{
int temp;
a=b;
b=temp;
}
int main()
{
int a=10;
int b=20;
func(&a,&b);
}
总结:如果不想修改主函数中的数据,用值传递,反之用地址传递。
十二,指针
1,字节大小
在32位操作系统下,撞针是占4个字节空间大小,不管是什么数据类型。
在64位操作系统下,指针是占8个字节空间大小。
2,指针作用
可以通过指针来保存一个地址。
3,空指针和野指针
(1)空指针用于给指针变量进行初始化。
例赋值时
int *p=NULL;
(2)空指针是不可以进行访问的。
(0~255之间的内存编号是系统占用的,因此不可以访间)
(3)空指针和野指针都不是我们申请的空间,因此不要访问。
4,coust修饰指针有三种情况:
(1)const修饰指针-- 常量指针。
例
coust int *p=&a;
*p=20;错误,指向的值不可以改。
p=&b正确,指针指向可以改.
特点:指针的1指向可以修改,但是指针指向的值不可以修改。
(2)const修饰常量— 指针常量。
例
int * const p=&a;
*p=20;正确, 指向的值可以改。
p=&b: 错误,指针指向不可以改。
特点:指针的指向不可以改,指针指向的值可以改。
(3)const即修饰指针,又修饰常星
例
inta=10;
intb=10;
int*p=&a;
const int*const p=&a;
*p=20; //错误。
p=&b: //错误。
特点:指针的指向和指针指向的值都不可以改。
十二,结构体基本概念
结构体属于用户自定义的数据类型,允许用户存储不同的数据类型。
1,结构体定义和使用
语法:struct 结构体名{结构体成员列表};
通过结构体创建变量的方式有三种:
(1)struct结构体名变量名例
struct Student s1
(2)struct结构体名变量名={成员1值,成员2…}例
struct Student s2={....}
(3)定义结构体时顺便创建变量,例
struct Student s3{成员1值,成员2..}chengji;
2,结构体定义和使用的总结
总结1: 定义结构体时的关键字是struct,不可省略。
总结2: 创建结构体变量时,关键字struct可以省略。
总结3:结构体变量利用操作符"."访问成员。
3,结构体指针
(1) 通过指针访问结构体变量中的数据
通过结构体指针访问结构体中的属性,需要利用“->”
例
struct student
{
string name;
int age;
string score;
}
int main ()
{
student s={"张三",18,100};
student *p=&s;
cout<<"姓名:"<<p->name<<"年龄"<<p->age<<"分数"<<p->score<<endl;
}
4.结构体中coust使用场景
作用:用const来防止误操作。
例const使用场景
void printStudent( const student*stu) //加const防止函数体中的误操作
{
stu->age=100; //操作失败,因为加了const修饰
cout <<“姓名:”<< stu->name <<”年龄:”<< stu->age << ”分数:”<< stu->score << endl;
}
5,结构体中的地址传递
struct Hare
{
string name;
int age;
string xb;
};
void bubbleSort (struct Hero heroArray[] ,int len)
{
for(inti=0;i<len-1;1++){
for(intj=0;j<1en-i-1;j++){
if (heroArray[j]. age〉heroArray[j + 1]. age)
{
struct Hero demp = heroArray[j];
struct Hero demp = heroArray[j];
heroArray[j] = heroArray[j + 1];
heroArray[j + 1] = temp;
}
}
}
}
int main()
{
struct Hare xinx[5]= { {"刘备",22,"男"},{"关羽",23,"男"},{"张飞",20,"男"},
{"赵云",21,"男"},{"貂蝉",19,"女"} };
bubbleSort(xinx);
for (int i = 0; i < 5; i++)
{
cout << xinx[i].name << xinx[i].age<<xinx[i].xb<< endl;
}
return 0;
}
数组名即是地址,因此本次实参到形参是地址传递非值传递。
通讯录管理系统
通讯录管理系统源代码
#define _CRT_SECURE_NO_WARNINGS 1
#include<iostream>
#include<string>
using namespace std;
void showMenu()
{
cout << "添加联系人请输1" << endl;
cout << "显示联系人请输2" << endl;
cout << "删除联系人请输3" << endl;
cout << "查找联系人请输4" << endl;
cout << "修改联系人请输5" << endl;
cout << "清空联系人请输6" << endl;
cout << "退出通讯录请输0" << endl;
}
void xiugai2()
{
cout << "如果修改联系人姓名请输1" << endl;
cout << "如果修改联系人性别请输2" << endl;
cout << "如果修改联系人年龄请输3" << endl;
cout << "如果修改联系人联系电话请输4" << endl;
cout << "如果修改联系人家庭住址请输5" << endl;
cout << "退出修改请输0" << endl;
}
struct lxr
{
string name;
string sex;
int age;
int phone;
string tiz;
};
struct Adds
{
struct lxr arr[1000];
int rs;
};
void tj(Adds *abs)//解码abs的地址将结构体Adds中的所有成员输入函数
{
int i=0;
if (abs->rs == 1000)
{
cout << "通讯录已满,无法添加" << endl;
return;
}
else {
string name;
string sex;
int age;
int phone;
string tiz;
cout << "请输入姓名" << endl;
cin >> name;
abs->arr[abs->rs].name = name;//变量abs即(结构体Abbs)中的arr数组中的第abs中的第rs个元素的name成员
cout << "请输入性别" << endl;
cin >> sex;
abs->arr[abs->rs].sex=sex;
cout << "请输入年龄" << endl;
cin >> age;
abs->arr[abs->rs].age = age;
cout << "请输入联系电话" << endl;
cin >> phone;
abs->arr[abs->rs].phone = phone;
cout << "请输入家庭住址" << endl;
cin >> tiz;
abs->arr[abs->rs].tiz = tiz;
abs->rs++;//更新通讯录人数
cout << "添加成功" << endl;
system("pause");//请按任意键继续
system("cls");//清屏
}
cout << endl;
}
void show(Adds * abs)
{
if (abs->rs == 0)
{
cout << "通讯录中没有人" << endl;
}
else
{
for (int i = 0; i < abs->rs; i++)//判断条件i<变量abs即(结构体Abbs)中rs即(人数)
{
cout << "姓名" << " " <<abs->arr[i].name << "\t";//变量abs即(结构体Abbs)中arr数组中第i个元素的name成员
cout << "性别" << " " <<abs->arr[i].sex << "\t";
cout << "年龄" << " " << abs->arr[i].age <<"\t";
cout << "联系电话" << " " <<abs->arr[i].phone <<"\t";
cout << "家庭住址" << " " <<abs->arr[i].tiz << endl;
}
}
system("pause");
system("cls");
}
int cazhao(Adds*abs,string name)//进入函数的参数1为 通讯录 参数2为 对比的姓名,方法可改进即(对比姓名改为对比电话号码)
{
for (int i = 0; i < abs->rs; i++)
{
if (abs->arr[i].name == name)
{
return i;//找到返回i即数组下标编号
}
}
return -1;//没找到返回-1
}
void deleteren(Adds*abs)
{
cout << "请输入你要删除的联系人" << endl;
string name1;
cin >> name1;
int b=cazhao(abs, name1);
if (b !=-1)//查到此人
{
for (int i = b; i < abs->rs; i++)
{
abs->arr[i] = abs->arr[i + 1];
}
abs->rs--;
cout << "删除成功" << endl;
}
else
{
cout <<"查无此人"<<endl;
}
system("pause");
system("cls");
}
void findren(Adds* abs)
{
cout << "请输入你要查找的联系人姓名" << endl;
string name2;
cin >> name2;
int c = cazhao(abs, name2);
if (c != -1)//找到了联系人
{
cout << "姓名" << abs->arr[c].name <<"\t";
cout << "性别" << abs->arr[c].sex <<"\t";
cout << "年龄" << abs->arr[c].age <<"\t";
cout << "联系电话" << abs->arr[c].phone <<"\t";
cout << "家庭地址" << abs->arr[c].tiz << endl;
}
else //没找到
{
cout << "查无此人" << endl;
}
system("pause");
system("cls");
}
void xiugai(Adds* abs)
{
cout << "请输入你要修改的联系人姓名" << endl;
string name3;
cin >> name3;
int d = cazhao(abs, name3);
if (d != -1)//找到了联系人
{
xiugai2();
int z;
cin >> z;
switch (z)
{
case 1:
{string name4;//姓名
cout << "请输入修改的名字" << endl;
cin >> name4;
abs->arr[d].name = name4;
cout << "联系人修改成功" << endl;
}; break;
case 2:
{string sex;//性别
cout << "请输入修改的性别" << endl;
cin >> sex;
abs->arr[d].sex = sex;
cout << "联系人修改成功" << endl;
}; break;
case 3:
{ int age;//年龄
cout << "请输入修改的年龄" << endl;
cin >> age;
abs->arr[d].age = age;
cout << "联系人修改成功" << endl;
}; break;
case 4:
{int phone;//联系电话
cout << "请输入修改的联系电话" << endl;
cin >> phone;
abs->arr[d].phone = phone;
cout << "联系人修改成功" << endl;
}; break;
case 5:{string tiz;//家庭住址
cout << "请输入修改的家庭地址" << endl;
cin >> tiz;
abs->arr[d].tiz = tiz;
cout << "联系人修改成功" << endl;
}; break;
case 0: {
cout << "欢迎下次使用" << endl;
}; break;
}
}
else//没找到联系人
{
cout << "查无此人" << endl;
}
system("pause");
system("cls");
}
void cleanren(Adds*abs)
{
cout << "是否确定清空通讯录,如果确定请输入“是”,否则请输入“否”" << endl;
string y;
cin >> y;
if (y =="是")
{
abs->rs = 0;//将当前联系人数量置为0,做一个逻辑上的清空
cout << "通讯录已清空" << endl;
system("pause");
system("cls");
}
else {
system("pause");
system("cls");
}
}
int main()
{
int a=0;
Adds abs;//定义结构体变量,故下方&abs为结构体Adds中的数据包含数组lxr和rs
abs.rs = 0;//先定义人数为0
while (true) {
showMenu();
cout << "请输入你的选择" << endl;
cin >> a;
switch (a)
{
case 1:tj(&abs); break;//向tj函数传递abs的地址即(结构体Adds中的所有成员)
case 2:show(&abs); break;
case 3:deleteren(&abs); break;
case 4:findren(&abs); break;
case 5:xiugai(&abs); break;
case 6:cleanren(&abs); break;
case 0:cout << "欢迎下次使用" << endl;
system("pause");
return 0;
}
}
system("pause");
return 0;
}
第一部分,程序主页面
void showMenu()
{
cout << "添加联系人请输1" << endl;
cout << "显示联系人请输2" << endl;
cout << "删除联系人请输3" << endl;
cout << "查找联系人请输4" << endl;
cout << "修改联系人请输5" << endl;
cout << "清空联系人请输6" << endl;
cout << "退出通讯录请输0" << endl;
}
并在主函数中进行调用即showMenu();
第二部分,创建结构体lxr和Adds
struct lxr
{
string name;
string sex;
int age;
int phone;
string tiz;
};
struct Adds
{
struct lxr arr[1000];
int rs;
};
1,其中lxr为存放联系人信息格式的结构体即(姓名,性别,年龄,联系电话,家庭住址)。
2,而Adds结构体中嵌套了结构体lxr并创建了结构体lxr类型的数组和整形变量re来存放人数。
第三部分,程序第一个功能输入联系人信息并存储
void tj(Adds *abs)//解码abs的地址将结构体Adds中的所有成员输入函数
{
int i=0;
if (abs->rs == 1000)
{
cout << "通讯录已满,无法添加" << endl;
return;
}
else {
string name;
string sex;
int age;
int phone;
string tiz;
cout << "请输入姓名" << endl;
cin >> name;
abs->arr[abs->rs].name = name;//变量abs即(结构体Abbs)中的arr数组中的第abs中的第rs个元素的name成员
cout << "请输入性别" << endl;
cin >> sex;
abs->arr[abs->rs].sex=sex;
cout << "请输入年龄" << endl;
cin >> age;
abs->arr[abs->rs].age = age;
cout << "请输入联系电话" << endl;
cin >> phone;
abs->arr[abs->rs].phone = phone;
cout << "请输入家庭住址" << endl;
cin >> tiz;
abs->arr[abs->rs].tiz = tiz;
abs->rs++;//更新通讯录人数
cout << "添加成功" << endl;
system("pause");//请按任意键继续
system("cls");//清屏
}
cout << endl;
}
1,函数名void tj(Adds *abs)//解码abs的地址将结构体Adds中的所有成员输入函数
注:abs为在主函数int main()中定义的结构体变量名,其中包含结构体Adds中的数据包含数组lxr和rs,并定义abs中的rs变量为0。
int a=0;
Adds abs;//定义结构体变量,故下方&abs为结构体Adds中的数据包含数组lxr和rs
abs.rs = 0;//先定义人数为0
2,用if语句判断abs中的rs变量中的人数是否达到1000达到则输出:通讯录已满,无法添加。
否则则添加联系人信息即一项一项输入,例:
string name;
cout << "请输入姓名" << endl;
cin >> name;
abs->arr[abs->rs].name = name;//变量abs即(结构体Abbs)中的arr数组中的第abs中的第rs个元素的name成员
abs->rs++
即将键盘输入的name赋值给变量abs即(结构体Abbs)中的arr数组中的第abs中的第rs个元素的name成员,最后人数要加一。
3,清屏代码
system("pause");//请按任意键继续
system("cls");//清屏
第四部分,展示全部联系人
void show(Adds * abs)
{
if (abs->rs == 0)
{
cout << "通讯录中没有人" << endl;
}
else
{
for (int i = 0; i < abs->rs; i++)//判断条件i<变量abs即(结构体Abbs)中rs即(人数)
{
cout << "姓名" << " " <<abs->arr[i].name << "\t";//变量abs即(结构体Abbs)中arr数组中第i个元素的name成员
cout << "性别" << " " <<abs->arr[i].sex << "\t";
cout << "年龄" << " " << abs->arr[i].age <<"\t";
cout << "联系电话" << " " <<abs->arr[i].phone <<"\t";
cout << "家庭住址" << " " <<abs->arr[i].tiz << endl;
}
}
system("pause");
system("cls");
}
1,先用if判断abs->rs中是否有人,如果没有输出通讯录中没有人,否则进入for循环,循环判断条件i<变量abs即(结构体Abbs)中rs即(人数)。
2,按顺序输出联系人信息,例:
cout << "姓名" << " " <<abs->arr[i].name << "\t";//变量abs即(结构体Abbs)中arr数组中第i个元素的name成员
注:\t为水平制表符
第五部分,创建查找联系人的函数
int cazhao(Adds*abs,string name)//进入函数的参数1为 通讯录 参数2为 对比的姓名,方法可改进即(对比姓名改为对比电话号码)
{
for (int i = 0; i < abs->rs; i++)
{
if (abs->arr[i].name == name)
{
return i;//找到返回i即数组下标编号
}
}
return -1;//没找到返回-1
}
1,给函数输入两个形参即参数1为 通讯录 参数2为 对比的姓名,方法可改进即(对比姓名改为对比电话号码)。
2,利用for循环遍历abs数组在用if语句判断即(abs->arr[i].name == name)找到则返回i即数组下标编号,若没找到则返回-1。
第六部分,删除的联系人
void deleteren(Adds*abs)
{
cout << "请输入你要删除的联系人" << endl;
string name1;
cin >> name1;
int b=cazhao(abs, name1);
if (b !=-1)//查到此人
{
for (int i = b; i < abs->rs; i++)
{
abs->arr[i] = abs->arr[i + 1];
}
abs->rs--;
cout << "删除成功" << endl;
}
else
{
cout <<"查无此人"<<endl;
}
system("pause");
system("cls");
}
1,先输入一个要查找的名字,利用第五部分的查找联系人的函数来进行查找。
2,用if语句来进行判断若变量b不等于-1则找到联系人并利用for循环将该联系人的信息用后面联系人的信息给覆盖,最后对abs人数减一。
3,如果if语句等于1则输出查无此人。
第七部分,查找联系人并输出
void findren(Adds* abs)
{
cout << "请输入你要查找的联系人姓名" << endl;
string name2;
cin >> name2;
int c = cazhao(abs, name2);
if (c != -1)//找到了联系人
{
cout << "姓名" << abs->arr[c].name <<"\t";
cout << "性别" << abs->arr[c].sex <<"\t";
cout << "年龄" << abs->arr[c].age <<"\t";
cout << "联系电话" << abs->arr[c].phone <<"\t";
cout << "家庭地址" << abs->arr[c].tiz << endl;
}
else //没找到
{
cout << "查无此人" << endl;
}
system("pause");
system("cls");
}
1,先输入一个要查找的名字,利用第五部分的查找联系人的函数来进行查找。
2,用if语句来进行判断若变量c不等于-1则找到联系人并输出联系人信息。
3,如果if语句等于1则输出查无此人。
第八部分,修改联系人
void xiugai(Adds* abs)
{
cout << "请输入你要修改的联系人姓名" << endl;
string name3;
cin >> name3;
int d = cazhao(abs, name3);
if (d != -1)//找到了联系人
{
xiugai2();
int z;
cin >> z;
switch (z)
{
case 1:
{string name4;//姓名
cout << "请输入修改的名字" << endl;
cin >> name4;
abs->arr[d].name = name4;
cout << "联系人修改成功" << endl;
}; break;
case 2:
{string sex;//性别
cout << "请输入修改的性别" << endl;
cin >> sex;
abs->arr[d].sex = sex;
cout << "联系人修改成功" << endl;
}; break;
case 3:
{ int age;//年龄
cout << "请输入修改的年龄" << endl;
cin >> age;
abs->arr[d].age = age;
cout << "联系人修改成功" << endl;
}; break;
case 4:
{int phone;//联系电话
cout << "请输入修改的联系电话" << endl;
cin >> phone;
abs->arr[d].phone = phone;
cout << "联系人修改成功" << endl;
}; break;
case 5:{string tiz;//家庭住址
cout << "请输入修改的家庭地址" << endl;
cin >> tiz;
abs->arr[d].tiz = tiz;
cout << "联系人修改成功" << endl;
}; break;
case 0: {
cout << "欢迎下次使用" << endl;
}; break;
}
}
else//没找到联系人
{
cout << "查无此人" << endl;
}
system("pause");
system("cls");
}
1,先输入一个要查找的名字,利用第五部分的查找联系人的函数来进行查找。
2,用if语句来进行判断若变量d不等于-1则找到联系人并输出修改列表xiugai2()并输入一个变量z进行修改选择。
3,利用switch语句进行修改选择。例:
case 1:
{string name4;//姓名
cout << "请输入修改的名字" << endl;
cin >> name4;
abs->arr[d].name = name4;
cout << "联系人修改成功" << endl;
}; break;
输入想改的姓名放入变量name4中并赋值给abs->arr[d].name。
4.如果if语句等于1则输出查无此人。
第九部分,清空联系人
void cleanren(Adds*abs)
{
cout << "是否确定清空通讯录,如果确定请输入“是”,否则请输入“否”" << endl;
string y;
cin >> y;
if (y =="是")
{
abs->rs = 0;//将当前联系人数量置为0,做一个逻辑上的清空
cout << "通讯录已清空" << endl;
system("pause");
system("cls");
}
else {
system("pause");
system("cls");
}
}
先用if语句进行判断确认是否要清空联系人,如果“是”则abs->rs = 0;//将当前联系人数量置为0,做一个逻辑上的清空。否则返回主菜单。
第十部分,主函数部分
int main()
{
int a=0;
Adds abs;//定义结构体变量,故下方&abs为结构体Adds中的数据包含数组lxr和rs
abs.rs = 0;//先定义人数为0
while (true) {
showMenu();
cout << "请输入你的选择" << endl;
cin >> a;
switch (a)
{
case 1:tj(&abs); break;//向tj函数传递abs的地址即(结构体Adds中的所有成员)
case 2:show(&abs); break;
case 3:deleteren(&abs); break;
case 4:findren(&abs); break;
case 5:xiugai(&abs); break;
case 6:cleanren(&abs); break;
case 0:cout << "欢迎下次使用" << endl;
system("pause");
return 0;
}
}
system("pause");
return 0;
}
1,定义主函数int main()中abs为结构体变量名,其中包含结构体Adds中的数据包含数组lxr和rs,并定义abs中的rs变量为0。
2,利用switch进行选择要进行的操作,输入一个变量a,然后判断。例
case 1:tj(&abs); break;//向tj函数传递abs的地址即(结构体Adds中的所有成员)
C++核心编程(内存和引用)
一,内存分区模型
C++程序在执行时,将内存大方向划分为4个区域
1,代码区:存放函数体的二进制代码,由操作系统进行管理的。
2,全局区:存放全局变量和静态变量以及常量。
3,全局区:存放全局变量和静态变量以及常量。
4,堆区:由程序员分配和释放若程序员不释放程序结束时由操作系统回收。
二,内存四区意义
不同区域存放的数据,赋予不同的生命周期,给我们更大的灵活编程。
三,程序运行前
即在程序编译后,生成了exe可执行程序,未执行该程序前分为两个区域。(每次运行程序都会重新分配内存)
1,代码区
(1)存放CPU执行的机器指令。
(2)代码区是共享的,共享的目的是对于频繁被执行的程序,只需要在内存中有一份代码即可。
(3)代码区是只读的,使其只读的原因是防止程序意外地修改了它的指令。
2,全局区
(1)全局变量和静态变量存放在此。
(2)全局区还包含了常量区,字符串常量和其他常量也存放在此。
(3)该区域的数据在程序结束后由操作系统释放。
例:
#include<iosteam>
using namespace std;
int a=10;
int main()
{
int b=20;
static c=30;
}
其中a为全局变量,b为局部变量,c为静态变量
全局区总结,1,局部变量、const修饰的局部变量(局部常量)(不在全局区中)。
2,全局变量,静态变量static关键字,常量,字符串常量,const修饰的全局变量(全局常量)(在全局区中)。
3,总结
(1),C++中在程序运行前分为全局区和代码区。
(2),代码区特点是共享和只读。
(3),全局区中存放全局变量、静态变星、常量。
(4),常量区中存放const修饰的全局常星和字符串常量。
四,程序运行后
1,栈区
(1),由编译器自动分配释放,存放函数的参数值,局部变量等。
(2),注意事项:不要返回局部变量的地址,栈区开辟的数据由编译器自动释放。
例
#define _CRT_SECURE_NO_WARNINGS 1
#include<iostream>
#include<string>
using namespace std;
int *func()栈区数据注意事项---不要返回局部变量的地址
{
int a = 10;
int *p = &a;//说明了指针*p只能存放地址
return p;//因为函数类型为int故返回的值只能为数字 而p是地址,改法1,将函数改为int*func()或将返回值改为*p
//故这返回的是局部变量的地址,所以在下面主函数int main()调用中出现错误
}
int main()
{
int *q = func();//因为func为指针类型返回地址故创建int*q来保存
cout << *q << endl; //第一次可以打印正确的数字,是因为编译器做了保留
cout << *q << endl;//第二次以后这个数据就不再保留了
cout << *q << endl;
return 0;
}
图示
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-b7kF05ej-1676032966224)(C:\Users\y’z\Desktop\C++\c++核心编程\屏幕截图 2022-11-12 110019.png)]
2,堆区
(1),由程序员分配释放,若程序员不释放,程序结束时由操作系统回收。
(2),在C++中主要利用new在堆区开辟内存。
例
#include<iostream>
using namespace std;
int *func()
}
//利用new关键字可以将数据开 辟到堆区
//指针γ本质也是局部变量,放在栈上,指针保存的数据是放在堆区
int *p=new int(10)
return p
}
int main()
{
//在堆区开辟数据
int*p=func();
return 0;
}
3,new操作符
(1),C++中利用new操作符在堆区开辟数据。
(2),堆区开辟的数据,由程序员手动开辟,手动释放,释放利用操作符delete。
(3),语法:new 数据类型。
(4),利用new创建的数据,会返回该数据对应的类型的指针。
例:new基础语法
int*func()
{
//在堆区创建整形数据
//new返回是 该数据类型的指针
int*p=new int(10);//或tz=new int(10) //并在main中写int*tz;
return p;
}
int main()
{
int*q=func();
cout<<*q<<endl;
cout<<*q<<endl;
cout<<*q<<endl;
//堆区的数据 由程序员开辟,程序员管理释放
//如果想释放堆区的数据,需利用关键字 delete
delete p;
//释放堆区数组
//释放数组的时候要加[]才可以
delete[]arr;
cout<<*q<<endl;
//内存已经被释放,再次访问为非法操作,会报错
}
五,引用的基本使用
作用 : 给变量起别名
语法 : 数据类型 &别名=原名
图示
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Ps5Ebmca-1676032966225)(C:\Users\y’z\Desktop\C++\c++核心编程\屏幕截图 2022-11-12 111855.png)]
例
int main()
{
int a;
int&b=a;//b就是a的别名
b=20;//也会改变a的数据
}
六,引用注意事项
1,引用必须初始化
2,引用在初始化后,不可以改变
例
int main()
{
int a=10;
int c=20;
int&b=a;//引用必须初始化即int &b//错误的
//引用一旦初始化后,不可以改变即int&b=a;后不可以int&b=c;
//并且int &b=a;相当于指针常量 int *coustp=a;
}
七,引用做函数参数
作用:函数传参时,可以利用|用的技术让形参修饰实参。
优点:可以简化指针修改实参。
引用传递,例
void func(int&a,int&b)
{
int temp;
a=b;
b=temp;
}
int main()
{
int a=10;
int b=20;
func(a,b)
}//引用传递,形参会修饰实参
八,引用做函数返回值
作用:引用是可以作为函数的返回值存在的。
注意:不要返回局部变量引用。
用法:函数调用作为左值。
例
//注意:不要返回局部变量引用。
int & test01()
{
int a=10;//局部变量存放在栈区
return a;
}
//函数调用作为左值。
int & test02()
{
static int a=10;//静态变量,存放在全局区,全局区上的数据在程序结束后系统释放
return a;
}
int main()
{
int &ref=test01();
test02() = 1000;//如果函数的返回值是引用,这个函数调用可以作为左值
cout<<"ref="<<ref<<endl;//第一次结果正确,是因为编译器做了保留
cout<<"ref="<<ref<<endl;//第二次结果错误,因为a的内存已经释放
}
图示
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-BVXVloAH-1676032966225)(C:\Users\y’z\Desktop\C++\c++核心编程\屏幕截图 2022-11-12 165603.png)]
九,常量引用
作用:常量引用主要用来修饰形参,防止误操作。
在函数形参列表中,可以const修饰形参,防止形参改变实参。
例
int main()
{
//加上const之后编译器将代码修改int temp = 10(系统自动创造一个变量temp); const int & ref = temp;
coust int &ref=10;
ref=20; //加入const之后变为只读,不可以修改
}
函数提高
一,函数默认参数
在C++中,函数的形参列表中的形参是可以有默认值的。
语法:返回值类型 函数名 (参数=默认值) {}
例
int func(int a,int b,int c=30)
{
return 0;
}
int main()
{
cout<<func(10,10)<<endl;//即最后的int c的形参可以不用填
system("pause");
return 0;
}
注意事项
1,如果某个位置已经有了默认参数,那么从这个位置往后,从左到右都必须有默认值。
例
int func2(int a = 10, int b,int C,int d) //如果int a有默认值则b,c,d都要有默认值
{
return 0;
}
2, 如果函数声明有默认参数,函数实现就不能有默认参数。
声明和实现只能有一个有默认参数。
例
int func2(int a,int b);//函数声明
//若为int func2(int a=10,int b=10)即错误
int func(int a=10,int b=10)//函数实现
{
return 0;
}
二,占位参数
返回值类型 函数名 (数据类型) {}。
占位参数,还可以有默认参数。
例
void func(int a,int)//即可以改为void func(int a,int=10)就可以在函数调用时只输入一个数字
{
cout<<"this is func"<<endl;
}
int main()
{
func(10,20);//因为函数中形参存在占位int故再调用函数时要输入两个数字不然会报错
system("pause");
return 0;
}
三,函数重载
1,函数重载概述
作用:函数名可以相同,提高复用性。
2,函数重载满足条件:
(1)同一个作用域下。
(2)函数名称相同。
(3)函数参数类型不同 或者 个数不同 或者 顺序不同。
例
void func()//1
{
cout<<"func的调用"<<endl;
}
void func(int a)//2
{
cout<<"func(int a)的调用"<<endl;
}
void func(double a)//3
{
cout<<"func(double a)的调用"<<endl;
}
void func(int a,double b)//4
{
cout<<"func (int a, double b) 的调用"<< endl;
}
void func (double a, int b)//5
{
cout <<"func (double a, int b)的调用"<<endl;
}
如图所示1,2,4,5为个数不同.
2和3为类型不同。
4和5为顺序不同。
注意事项,1,函数的返回值不可以作为函数重载的条件
2, 引用作为重载的条件
例
void func(int &a) // int &a = 10;不合法但是可以调用int &a=a
{
cout<<"func(int &a)调用"<<endl;
}
void func(const int &a) //const int &a = 10; 合法
{
coust<< "func(const int &a)调用"<< endl;
}
int main
{
int a=10;
func(a);//调用无const
func(10);//调用有const
}
3, 函数重载碰到默认参数
错误例子
void func2(int a,int b = 10)
{
cout <<"func2(int a,int b)的调用"<< endl;
}
void func2(int a)
{
cout<<" func2(int a )的调用"<< endl;
}
当函数重载碰到默认参数,出现二义性,报错,尽量避免这种情况。
四,类和对象
1,C++面向对象雨三大特性为:封装、继承、多态。
2,C++认为万事万物都皆为对象,对象上有其属性和行为。
例
1,人可以作为对象,属性有姓名、年龄、身高体重…行为有走、跑、跳吃饭、唱歌…
2,也可以作为对象,属性有轮胎、向盘、…行为有载人。放音乐、放空调…
3,具有相同性质的对象,我们可以抽象称为类,人属于人类,车属于车类。
1,封装
1,封装的意义
1,封装是C++面向对象三大特性之一。
2,封装的意义:
(1)将属性和行为作为一个整体,表现生活中的事物。
(2)将属性和行为加以权限控制。
2,封装意义一
在设计类的时候,属性和行为写在一起,表现事物
语法: class 类名{访问权限: 属性/ 行为};
例
#include<iostream>
using namespace std;
class yun
{
public://访问权限公共权限
int m_r;//属性
double cocang()//行为
{
return 2*pi*m_r;
}
}
int main()
{
//通过圆类创建具体的圆(对象)
yun c1;
//给圆对象的属性进行赋值
c1.m_r=10;
}
行业黑话
1,类中的属性和行为我们统一称为 成员
2,属性 成员属性成员变量
3,行为 成员函数成员方法
3,封装意义二(三种权限)
1,类在设计时,可以把属性和行为放在不同的权限下,加以控制
2,访问权限有三种:
(1)public 公共权限(成员 类内可以访问 类外可以访问)
(2)protected 保护权限 (成员 类内可以访问 类外不可以访问 例如儿子可以访问父亲中的保护内容)
(3)private 私有权限(成员 类内可以访问 类外不可以访问 例如儿子不可 以访问父亲的私有内容)
使用方法 例
class Person
{
public:
//公共权限
string m_Name; // 姓名
protected:
//保护权限
string m_Car; //汽车
private:
//私有权限
int m_Password; //银行卡密码
}
int main()
{
Person pl;
p1.m_Name = "李四”:
p1.m_Car =”奔驰”: //保护权限内容,在类外访问不到
p1.m_Password = 123://私有权限内容,类外访问不到
system(" pause" );
return 0;
}
4,struct和class区别
1,在C++中struct和class唯一的区别就在于 默认的访问权限不同。
2,区别:
(1)struct默认权限为公共。
(2)class 默认权限为私有。
5,成员属性设置为私有
优点1:将所有成员属性设置为私有,可以自己控制读写权限。
优点2:对于写权限,我们可以检测数据的有效性。
例
class xinx
{
public://设置姓名 故名字可读可写 若只有上部分则只可写,若只有下部分则只读
void setName(string name)
{
m_Name=name
}
//获取姓名
string getName()
{
return m_name
}
}
案例1 立方体
源代码
#define _CRT_SECURE_NO_WARNINGS 1
#include<iostream>
#include<string>
using namespace std;
class cube
{
public:
void setl(int l//设置长 下一致
{
m_l = l;
}
int getl()//获取长 下一致
{
return m_l;
}
void seth(int h)
{
m_h = h;
}
int geth()
{
return m_h;
}
void setw(int w)
{
m_w = w;
}
int getw()
{
return m_w;
}
public:
int mj()//计算面积
{
return (2 * (m_l * m_w + m_l * m_h + m_w * m_h));
}
int tj()//计算体积
{
return m_l * m_h * m_w;
}
//利用成员函数判断是否相当
bool issame2(cube z)
{
if (m_h == z.geth() && m_w == z.getw() && m_l == z.getl())
{
return true;
}return false;
}
private:
int m_l;
int m_w;
int m_h;
};
//利用全局函数判断是否相当
bool issame(cube z1, cube z2)//利用bool类型返回1或0
{
if (z1.geth() == z2.geth() && z1.getw() == z2.getw() && z1.getl() == z2.getl())
{
return 1;
}
return 0;
}
int main()
{
cube z1;
cube z2;
z1.seth(10);//设定立方体z1初始值
z1.setl(10);
z1.setw(6);
cout<<"c1面积为" << z1.mj() << endl;;
cout<<"c1体积为"<<z1.tj()<<endl;
z2.seth(10//设定立方体z2初始值
z2.setl(10);
z2.setw(10);
cout << "c2面积为" << z2.mj() << endl;;
cout << "c2体积为" << z2.tj() << endl;
//全局
bool ret = issame(z1, z2);
if (ret==1)
{
cout << "z1和z2相等"<< endl;
}
else {
cout << "z1和z2不相等" << endl;
}
//成员
bool a = z1.issame2(z2);//直接用z1的issame2和z2进行比较故只用输入一个形参
cout << a << endl;
if (a)
{
cout << "z1和z2相等" << endl;
}
else {
cout << "z1和z2不相等" << endl;
}
return 0;
}
注意事项
成员函数判断
bool a = z1.issame2(z2);
若改为
bool a = issame2(z2);
则会报错 因为issame2在封装内故要用z1.进行调用因为已经用z1或z2进行调用所以只用在函数内填入一个参数(z1或z2)
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-D8b3K0rN-1676032966226)(C:\Users\y’z\Desktop\C++\c++核心编程\屏幕截图 2022-11-13 181416.png)]
案例2 点和圆的关系
#define _CRT_SECURE_NO_WARNINGS 1
#include<iostream>
#include<string>
#include<math.h>
using namespace std;
class Point
{
public:
void setX(int x)
{
m_X = x;
}
int getX()
{
return m_X;
}
void setY(int y)
{
m_Y = y;
}
int getY()
{
return m_Y;
}
private:
int m_X;
int m_Y;
};
class Circle
{
public:
void setR(int r)
{
m_R = r;
}
int getR()
{
return m_R;
}
void setxinx(Point xinx)
{
m_Center = xinx;
}
Point getxinx()
{
return m_Center;
}
private:
int m_R;//半径
Point m_Center;
};
void isinCircle(Circle&c,Point&p)
{
float a,b;
a = pow((c.getxinx().getX() - p.getX()), 2) + pow((c.getxinx().getY() - p.getY()), 2);
b = c.getR() * c.getR();
if (a > b) {
cout << "点在圆外" << endl;
}
else if (a == b)
{
cout << "点在圆上" << endl;
}
else if (a < b) {
cout << "点在圆内" << endl;
}
}
int main()
{
Circle c;
c.setR(10);
Point d;//即命名圆心
d.setX(10);//即给圆心参数
d.setY(0);
c.setxinx(d);
Point p;
p.setX(10);
p.setY(9);
isinCircle(c, p);
}
注意事项1
#define _CRT_SECURE_NO_WARNINGS 1
#include<iostream>
#include<string>
#include<math.h>
<math.h>数学函数库,在此作用为后面使用pow函数即平方根函数
a = pow((c.getxinx().getX() - p.getX()), 2) + pow((c.getxinx().getY() - p.getY()), 2);
注意事项2
class Circle
{
public:
void setR(int r)
{
m_R = r;
}
int getR()
{
return m_R;
}
void setxinx(Point xinx)
{
m_Center = xinx;
}
Point getxinx()
{
return m_Center;
}
private:
int m_R;//半径
Point m_Center;
};
封装中可以建立其他封装的内容函数即在类中可以让另一个类作为成员
void setxinx(Point xinx)
{
m_Center = xinx;
}
Point getxinx()
{
return m_Center;
}
圆与点关系拓展(重点)
我们可以再创建一个头文件和源文件
作用:在大型项目中可以更加清晰制作项目
例point.h(头)
并在头文件中输入#pragma once
并在头文件中将点类函数声明放入其中例
#pragma once
#include<iostream>
using namespace std;
class Point
{
public:
void setX(int x)
int getX()
void setY(int y)
int getY()
private:
int m_X;
int m_Y;
};
在本例中创建圆类的源文件中因为圆类中存在点类故需加入#include"point.h"
其他部分类似
point.cpp(源)
并在源文件中输入#include"point.h"
并在源文件中将点类函数实现放入其中例
但是要在命名前输入 Point::即(定义域)
void Point::setX(int x)
{
m_X = x;
}
int Point::getX()
{
return m_X;
}
void Point::setY(int y)
{
m_Y = y;
}
int Point::getY()
{
return m_Y;
}
2,对象的初始化和清理
1,生活中我们买的电子产品都基本会有出厂设置,在某-天我们不用时候也会删除一些自己信息 数据保证安全
2,C++中的面向对象来源于生活,每个对象也都会有初始设置以及对象销毁前的清理数据的设置。
1,构造函数和析构函数
1,对象的初始化和清理也是两个非常重要的安全问题
(1)一个对象或者变是没有初始状态,对其使用后果是未知。
(2)同样的使用完一个对象或变量,没有及时清理,也会造成一定的安全问题。
2,C++利用了构造函数和析构函数解决上述问题,这两个函数将会被编译器自动调用,完成对象初始化和清理工作。
3,对象的初始化和清理工作是编译器强制要我们做的事情,因此如果我们不提供构造和析构,编译器会提供。
4,编译器提供的构造函数和析构函数是空实现。
2,作用
构造函数:主要作用在于创建对象时为对象的成员属性赋值,构造函数由编译器自动调用,无须手动调用。
析构函数:主要作用在于对象销毁前系统自动调用,执行一些清理工作。
3,语法
构造函数语法:类名(){}
1.构造函数,没有返回值也不写void。
2.函数名称与类名相同。
3.构造函数可以有参数,因此可以发生重载。
4.程序在调用对象时候会自动调用构造,无须手动调用,而且只会调用一次。
例
class person
{
public:
person()
{
cout<<"person 构造函数的调用"<<endl;//你不写他就空实现即没有东西输出你写了就用你的
}
}
void test01()
{
Person p; //只是创建了一个对象并没有调用,在栈上的数据,test01执行完毕后,释放这个对象
}
int main()
{
test01()
system("pause")
return 0;
}
析构函数语法:类名(){}
1.析构函数,没有返回值也不写void。
2.函数名称与类名相同,在名称前加上符号~即(esc键下方)。
3.析构函数不可以有参数,因此不可以发生重载。
4.程序在对象销毁前会自动调用析构,无须手动调用,而且只会调用一次。
例
class person
{
~person()
{
cout<<"person的析构函数"<<endl;
}
}
void test01()
{
Person p; //只是创建了一个对象并没有调用,在栈上的数据,test01执行完毕后,释放这个对象即自动调用析构函数
}
int main()
{
test01();
person p;//创建在main()中则在请输入任意键继续后才调用一瞬间
system("pause")
return 0;
}
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-vTd0oOBL-1676032966226)(C:\Users\y’z\Desktop\C++\c++核心编程\屏幕截图 2022-11-14 211352.png)]
构造和析构都是必须有的实现,如果我们自己不提供,编译器会提供一个空实现的构造和析构。
4,构造函数的分类及调用
两种分类方式:
(1)按参数分为:有参构造和无参构造(无参又称为默认构造函数)。
(2)按类型分为:普通构造和拷贝构造。
三种调用方式:
(1)括号法
(2)显示法
(3)隐式转换法
例,分类方式
class Person
{
Person()
{
cout<<"person的无参构造调用"<<endl;
}
Person(int a)
{
cout<<"Person的有参构造函数的调用"<<endl;
}
//拷贝构造函数
Person(const Person &p)
{//将传入的人身上的所有属性,拷贝到我身上
age=p.age;
}
int p;
}
例,调用方式
1,括号法
Person p1; //默认构造函数调用
Person p2(10); //有参构造函数
Person p3(p2); //拷贝构造函数
注意事项
调用默认构造函数时候,不要加()
因为下面这行代码,编译器会认为是一个函数的声明,不会认为在创建对象
Person p1()
2、显示法
Person p1;
Person p2=Person(10);//有参构造
Person P3=Person(P2);//拷贝构造
//单个时为匿名对象
//Person(10); //匿名对象特点: 当前行执行结束后,系统会立即回收掉匿名对象
注意事项2
不要利用 拷贝构造函数 初始化匿名对象 编译器会认为 Person (p3) == Person p3;对象声明
(匿名对象就是没有名字的对象,没有对象名的对象)
作用:
(1)提高开发效率;匿名对象当前行使用之后,如果没有其他引用数据类型的变量保存其地址,直接销 毁;
(2)简化代码结构
(3)通过匿名对象直接调用成员方法;
(4)使用匿名对象作为方法的参数;
Person p5(p4);
5,拷贝构造函数调用时机
C++中拷贝构造函数调用时机通常有三种情况
(1)使用一个已经创建完毕的对象来初始化一个新对象。
(2)值传递的方式给函数参数传值。
(3)以值方式返回局部对象。
//1、 使用一个己经创建完毕的对象来初始化一个新对象
class Person
{
public:
Person(int a)
{
age=a;
}
Person(const Person &p)
{
age=p.age;//要用p.引用age
}
public:
int age;
};
void test01()
{
Person p1(20) ;
Person p2(p1) ;
cout <<"P2的年龄为"<< p2.Age 《endl;
}
//2. 值传递的方式给函数参数传值
void doWork(Person p)//值传递会拷贝一个临时的副本即调用了拷贝构造函数
{
}
void test02()
{
Person p;
doWord(p);
}
//方式返回局部对象
Person dowork02()
{
person P1;//并不是返回P1,而是拷贝一个P1返回
cout<<(int*)&p1<<endl;
return p;//因为返回值为p1即Person中成员故要用Person类型接收
}
void test03()
{
Person p=dowork2();
}
6,构造函数调用规则
默认情况下,C++编译器至少给一个类添加3个函数
(1)默认构造函数(无参,函数体为空即空实现 )。
(2)默认析构函数(无参,函数体为空即空实现)。
(3)默认拷贝构造函数,对属性进行值拷贝。
例(3)
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-scGChhOT-1676032966226)(C:\Users\y’z\Desktop\C++\屏幕截图 2022-11-18 193010.png)]
没写拷贝构造函数但他默认提供了m_Age=p.m_Age;
构造函数调用规则如下:
(1)如果用户定义有参构造函数,C++不在提供默认无参构造,但是会提供默认拷贝构造。
如果没有默认构造函数则无法调用
Person p;//创建一个p类名
(2)如果用户定义拷贝构造函数,C++不会再提供其他构造函数。
7, 深拷贝与浅拷贝
浅拷贝:简单的赋值拷贝操作。
深拷贝:在堆区重新申请空间,进行拷贝操作。
例
class person
{
public:
person()
{
cout << "person 无参函数的调用" << endl;
}
person(int p,double c)
{
age = p;
height = new double(c);
cout << "person 有参函数的调用" << endl;
}
//自己实现拷贝构造函数解决浅拷贝带来的问题即系统自带的拷贝构造函数无法运行析构内代码
person(const person &p)
{
age=p.age
//height = p.height;编译器默认实现就是这行代码
//深拷贝操作
height = new int (*p.height);
}
~person()
{
//析构代码,将堆区开辟数据做释放操作
if(height!=NULL)
{
delete height;
height=NULL;
}
cout << "person的析构函数的调用" << endl;
}
int age;
double* height;
};
void test01()
{
person p1(18,160);//即浅拷贝
//栈区的数据先进后出
person P2(P1)
cout << p1.age <<" " << *p1.height << endl;
cout << p2.age <<" " << *p2.height << endl;
}
int main()
{
test01();
return 0;
}
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-dCSOCYXF-1676032966226)(C:\Users\y’z\Desktop\C++\c++核心编程\屏幕截图 2022-11-17 125258.png)]
总结: 如果属性有在堆区开辟的,一定要自己提供拷贝构造函数,防止浅拷贝带来的问题。
8,初始化列表
作用:
C++提供了初始化列表语法,用来初始化属性
语法:构造函数(): 属性1(值1),属性2 (值2) … {}
//传统初始化操作
person(int a,int b,int c)
{
A=a;
B=b;
C=c;
}
person():A(10),B(20),C(30)
{
}
person(int a,int b,int c,)A(a),B(b),C(c)
int A;
int B;
int C;
9, 类对象作为类成员
C++类中的成员可以是另一个类的对象,我们称该成员为对象成员
例
class Phone
{
public:
Phone(string pName)
{
m_PName=PName;
}
string m_PName;
};
class person
{
public:
person(string name,string pName):m_Name(name),m_phone(pName)//m_phone m_Phone=pName
};
string m_Name;
Phone m_Phone;
void test01()
{
person p("张三","苹果")
}
person 类中有对象Phone作为成员,Phone为对象成员。
那么当创建B对象时,A与B的构造和析构的顺序是谁先谁后?
当其他类对象作为本类成员,构造时候先构造类对象,再构造自身,析构的顺序与构造相反
即在本例中先构造手机在构造人
10,静态成员
静态成员就是在成员变量和成员函数前加.上关键字static,称为静态成员。
静态成员分为:
(1)静态成员变量
所有对象共享同一份数据。
在编译阶段分配内存。
类内声明,类外初始化。
例
class Person
{
public:
//所有对象共享同一份数据。
static int m_A;//类内声明
//静态成员变量也有访问权限的
private:
static int m_B;
}
int Person::m_A=100;//类外初始化
int Person::m_B=200;
void test01()
{
//静态成员变量不属于某个对象上,所有对象都共享同一份数据
//因此静态成员变量有两种访问方式
//1、通过对象进行访问
Person p;
//cout<<p.m_A<< endl;
//2、 通过类名进行访问
cout < Person::m_A << endl;
//cout < Person::m_B << endl;//不可以访问因为为私有权限
}
int main()
{
}
(2)静态成员函数
所有对象共享同一个函数。
静态成员函数只能访问静态成员变量。
例
class Person
{
public:
static void func()//静态成员函数
{
m_a=100;//静态成员函数能访问静态成员变量。因为为静态成员变量大家都用同一份
m_B=200;//马上报错因为m_B非静态成员变量,无法区分到底是那个对象的m_B属性例如果p1和p2同时调用无法区分
cout<<"static void func()的调用"<<endl;
//静态成员函数也有访问权限的
private:
static void func2()
{
cout<<"static void func2()的调用"<<endl;
}
}
static int m_A;
int m_B;
}
int Person::m_A=0;
void test01()
{
//通过对象进行访问
Person p;
p.func();
//通过类名进行访问
Person::func();
Person::func2()//类外访问不到私有静态成员函数
}
int main()
{
}
3,C++对象模型和this指针
1,成员变量和成员函数分开存储
在C++中,类内的成员变量和成员函数分开存储。
只有非静态成员变量才属于类的对象上。
注意事项
空对象占用内存空间为: 1。
C++编译器会给每个空对象也分配一个字节空间,是为了区分空对象占内存的位置。
每个空对象也应该有一个独一无二的内存地址。
2, this指针概念
(1)指针的定义
每一个非静态成员函数只会诞生一份函数实例, 也就是说多个同类型的对象会共用一块代码
C++通过提供特殊的对象指针,this指针, 解决上述问题。this指针指向被调用的成员函数所属的对象
this指针是隐含每一个非静态成员函数内的一种指针
this指针不需要定义,直接使用即可
(2)this指针的用途: .
当形参和成员变量同名时,可用this指针来区分。
例
class Person
{
public:
Person(int age)
{
//this指针指向 被调用的成员函数所属的对象
this->age=age;//当形参和成员变量同名时,可用this指针来区分因为this为指针所以要用this->来引用
}
int age;
};
void test01()
{
Person p1();
cout<<p.age<<endl;
}
int main()
{
}
链式编程思想出现
在类的非静态成员函数中返回对象本身,可使用return *this。即链式编程思想
例
class Person
{
public:
Person(int age)
{
this->age=age;
}
int age;
Person& PersonAddAge(Person &p)
{
this->age+=p.age
return *this;//因为返回值为this指针即p2所以要用Person类型来接收,返回本体用&
//的方式返回,如果不用&则返回p2的值,则会拷贝一份新的数据并不会继续引用
//this指向p2的指针,而*this指向的就是p2这个对象本体
}
};
void test01()
{
Person p1(10);
Person p2(10);//当p2去调用函数时this指向p2故函数返回为p2时可以继续调用函数
p2.PersonAddAge(p1).PersonAddAge(p1).PersonAddAge( p1);//即链式编程思想
cout<<p2.age<<endl;
}
int main()
{
}
3,空指针访问成员函数
C++中空指针也是可以调用成员函数的,但是也要注意有没有用到this指针
如果用到this指针,需要加以判断保证代码的健壮性
在函数内打印成员都默认加了this->
例
void showPersonAge()
{
//报错原因 是因为传入的指针是为NULL
if(this==NULL)
{
return;
}
cout<<age<<endl;==cout<<this->age<<endl;
}
void test01()
{
Person *p=NULL;
p->showPersonAge;
}
4, const修饰成员函数
常函数:
(1)成员函数后加const后我们称为这个函数为常函数
(2)常函数内不可以修改成员属性
(3)成员属性声明时加关键字mutable后,在常函数中依然可以修改
常对象:
(1)声明对象前加const称该对象为常对象
(2)声明对象前加const称该对象为常对象
例
class Person
{
public:
//this指针的本质是 指针常量 指针的指向是不可以修改的
//即Person *const this;如果后加const则为const Person *const this
void showPerson()const//即在后面写const后就无法修改m_A
{
this->m_A=100;//即报错
}
int m_A
mutable int m _B: //特殊变量,即使在常函数中,也可以修改这个值,因为关键字mutable
};
void test01()
{
const Person p; //在对象前加const,变为常对象
p.m_A=100;
p.m_B = 100; //m_ B是特殊值,在常对象下也可以修改
//常对象只能调用常函数
p.showPerson() ;
//p. func(): //常对象 不可以调用普通成员函数,因为普通成员函数可以修改属性|
}
int main()
{
}
4,友元
友元的目的就是让一个函数或者类访问另一个类中私有成员
友元的关键字为friend。
友元的三种实现
1,全局函数做友元
即在类中前面添加
friend void goodGay (Building *building);
即goodGay全局函数是Building好朋友,可以访问Building中私有成员
2,类做友元
目的为了可以样一个类访问另一个类的私有成员
即在包含类中前面添加被包含的类名称
friend class GoodGay ;
GoodGay类是本来的好朋友,可以访问本类中私有成员|
类外写成员函数
Building::Building()//类中要包含Building()声明
{
m_SittingRoom ="客厅" ;
m_BedRoom ="卧室" ;|
}
3,成员函数做友元
即在包含类中前面添加被包含的类的成员函数名称
friend void GoodGay::visit();
告诉编译器 GoodGay类下的visit成员函数作为本类的好朋友,可以访问私有成员
5,运算符重载
运算符重载概念:对已有的运算符重新进行定义,赋予其另一种功能,以适应不同的数据类型
1,加号运算符重载
作用:实现两个自定义数据类型相加的运算关键字**(operator+)**
例
class Person
{
public:
int m_A;
int m_B;
};
void test01()
{
Person p1;
Person p2;
p1.m_A=10;
p1.m_B=10;
p2.m_A=10;
P2.m_B=10;
Person p3=p1+p2;
cout<<"p3.m_A="<<p3.m_A<<endl;
cout<<"p3.m_B="<<p3.m_B<<endl;
}
//全局函数重载+号
Person operator+(Person &p1,Person &P2)
{
Person temp;
temp.m_A=p1.m_A+p1.m_B;
return temp;
}
int main()
{
tset01();
return 0;
}
2,左移运算符重载(运用了链式结构)
作用:可以输出自定义数据类型关键字**(operator<<)和cout的数据类型为(ostream)**
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-11mX3ZUm-1676032966227)(C:\Users\y’z\Desktop\C++\c++核心编程\屏幕截图 2022-11-20 103537.png)]
故只能用全局函数来定义左移运算符重载
class Person
{
friend ostream& operator<<(ostream& cout, Person& p);
friend void test01();//用二个友元开放权限
public:
private:
int m_A;
int m_B;
};
ostream& operator<<(ostream& cout, Person& p)//链式思想来使其不断的返回cout使test01的cout<<p<<endl;代码不报错
{
cout << "m_A=" << p.m_A << " m_B=" << p.m_B;
return cout;
}
void test01()
{
Person p;
p.m_A = 10;
p.m_B = 10;
cout << p << endl;
}
int main()
{
test01();
return 0;
}
3,递增运算符重载(递减)
作用:通过重载递增运算符,实现自己的整型数据(递减基本一致)
前置递增一定要返回值, 后置递增一定要返回引用
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-tVYkn0mE-1676032966227)(C:\Users\y’z\Desktop\C++\c++核心编程\屏幕截图 2022-11-20 110752.png)]
class Person
{
friend ostream& operator<<(ostream& cout, Person p);
friend void test01();//用二个友元开放权限
public:
//void operator++(int) int代表占位参数, 可以用于区分前置和后置递增
Person operator++(int)//后置递增++
{
Person temp;
temp = *this;//先保存数据
m_A++;//然后再++
return temp;//返回局部变量temp,用值接收因为temp为栈区数据用完清除
}
Person& operator++()//前置递增++
{
++m_A;
return *this;//this指针返回最近的对象即++m_A,要用引用接收,不然则会重新拷贝一份数据
}
private:
int m_A=0;
int m_B;
};
ostream& operator<<(ostream& cout, Person p)//链式思想来使其不断的返回cout使test01的cout<<p<<endl;代码不报错
{
cout << p.m_A<< endl;
return cout;
}
void test01()
{
Person p1;
cout << ++p1 << endl;
cout << p1 << endl;
}
void test02()
{
Person p;
cout << p++ << endl;
cout << p << endl;
}
int main()
{
test01();
test02();
return 0;
}
4,赋值运算符重载
C++编译器至少给一个类添加4个函数
1.默认构造函数(无参,函数体为空)
2.默认析构函数(无参,函数体为空)
3.默认拷贝构造函数,对属性进行值拷贝
4.赋值运算符operator=,对属性进行值拷贝
如果类中有属性指向堆区,做赋值操作时也会出现深浅拷贝问题
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-7vVxcVG0-1676032966227)(C:\Users\y’z\Desktop\C++\c++核心编程\屏幕截图 2022-11-20 125718.png)]
例
class Person
{
public:
Person(int age)
{
m_age = new int(age);//浅拷贝
}
~Person()
{
if (m_age != NULL)//自主释放堆区内存即上步new int(age)中的内存
//但是因为浅拷贝问题会崩故需要重载=号并利用深拷贝
{
delete m_age;
m_age = NULL;
}
}
Person& operator=(Person& p)
{
if (m_age != NULL)
{
delete m_age;
m_age = NULL;
}
m_age = new int(*p.m_age);//深拷贝
return *this;//返回最近的对象在本例中为p3
}
public:
int *m_age;
};
void test01()
{
Person p1(18);
Person p2(19);
Person p3(20);
p1 = p2 = p3;
cout << *p1.m_age<<" " << *p2.m_age <<" " << *p3.m_age << endl;
}
int main()
{
test01();
return 0;
}
5,关系运算符重载
作用:重载关系运算符,可以让两个自定义类型对象进行对比操作
例
class Person
{
friend bool operator!=(Person& p, Person& q);//友元
public:
Person(string name, int age)
{
m_Name = name;
m_Age = age;
}
bool operator==(Person &p)//局部函数重载
{
if (this->m_Name == p.m_Name && this->m_Age == p.m_Age)//即如果我自身的m_Name==传进来的p.m_Name
{
return true;
};
return false;
}
private:
string m_Name;
int m_Age;
};
bool operator!=(Person &p, Person &q)//全局函数重载
{
if (p.m_Name != q.m_Name || p.m_Age != q.m_Age)//如果m_Name和m_Age有一个不一样则返回true
{
return true;
};
return false;
}
void test01()
{
Person p1("张三",18);
Person p2("李四", 18);
if (p1 == p2)
{
cout << "p1和p2相等" << endl;
}
else cout << "p1和p2不相等" << endl;
}
void test02()
{
Person p1("张三", 18);
Person p2("李四", 18);
if (p1 != p2)
{
cout << "p1和p2不相等" << endl;
}
else cout << "p1和p2相等" << endl;
}
int main()
{
test01();
test02();
return 0;
}
6,函数调用运算符重载
函数调用运算符()也可以重载
由于重载后使用的方式非常像函数的调用,因此称为仿函数
仿函数没有固定写法,非常灵活(在类内)
例
class Person
{
public:
int operator()(int num1, int num2)
{
return num1 + num2;
}
};
void test01()
{
Person p;
int ret=p(100, 100);
cout << ret << endl;
}
int main()
{
test01();
return 0;
}
6,继承
继承是面向对象三大特性之一
图示
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-331Q90sg-1676032966228)(C:\Users\y’z\Desktop\C++\c++核心编程\屏幕截图 2022-11-20 172818.png)]
我们发现,定义这些类时,下级别的成员除了拥有上一级的共性,还有自己的特性。
这个时候我们就可以考虑利用继承的技术,减少重复代码
1,继承的基本语法
继承的好处:减少重复代码
语法:class 子类 : 继承方式 父类
子类也称为派生类
派生类中的成员,包含两大部分:
一类是从基类继承过来的,一类是自己增加的成员。
从基类继承过过来的表现其共性,而新增的成员体现了其个性。
父类也称为基类
例
class BasePage
{
public:
void header() {
cout << "首页、公开课、登录、注册...(公共头部)" << endl;
}
void footer() {
cout << "帮助中心、交流合作、站内地图...(公共底部)" << endl;
}
void left()
{
cout << "Java、Python、C++、...(公共分类列表)" << endl;
}
};
class Java:public BasePage//即继承方法
{
public:
void content()
{
cout << "Java学科视频" << endl;
}
};
class Python :public BasePage//即继承方法
{
public:
void content()
{
cout << "Python学科视频" << endl;
}
};
class CPP :public BasePage//即继承方法
{
public:
void content()
{
cout << "C++学科视频" << endl;
}
};
void test01()
{
cout << "Java下载视频页面如下:" << endl;
Java ja;
ja.header();
ja.footer();
ja.left();
ja.content();
cout << "---------------------" << endl;
cout << "C++下载视频页面如下:" << endl;
CPP cpp;
cpp.header();
cpp.footer();
cpp.left();
cpp.content();
cout << "---------------------" << endl;
cout << "Python下载视频页面如下:" << endl;
Python py;
py.header();
py.footer();
py.left();
py.content();
}
int main()
{
test01();
return 0;
}
2,继承方式
继承的语法: class子类∶继承方式父类
继承方式—共有三种:
公共继承(成员 类内可以访问 类外可以访问)
保护继承(成员 类内可以访问 类外不可以访问)
私有继承(成员 类内可以访问 类外不可以访问 )
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-7xarMFq6-1676032966228)(C:\Users\y’z\Desktop\C++\c++核心编程\屏幕截图 2022-11-20 180511.png)]
3,继承中的对象模型
问题:从父类继承过来的成员,哪些属于子类对象中?
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-xGkgldZ3-1676032966228)(C:\Users\y’z\Desktop\C++\c++核心编程\屏幕截图 2022-11-20 193247.png)]
打开vs开发人员命令工具
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-aGb8b7Ph-1676032966229)(C:\Users\y’z\Desktop\C++\c++核心编程\屏幕截图 2022-11-20 193956.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-2Fd7Eco0-1676032966229)(C:\Users\y’z\Desktop\C++\c++核心编程\屏幕截图 2022-11-20 193722.png)]
4,继承中构造和析构顺序
子类继承父类后,当创建子类对象,也会调用父类的构造函数
问题:父类和子类的构造和析构顺序是谁先谁后?
继承中的构造和析构顺序如下:
先构造父类,再构造子类,析构的顺序与构造的顺序相反
(和类嵌套一样)
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-PyePffsN-1676032966229)(C:\Users\y’z\Desktop\C++\c++核心编程\屏幕截图 2022-11-20 194757.png)]
5,继承同名成员处理方式
问题:当子类与父类出现同名的成员,如何通过子类对象,访问到子类或父类中同名的数据呢?
同名成员属性处理
访问子类同名成员直接访问即可
图示
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-tTBxtoPP-1676032966229)(C:\Users\y’z\Desktop\C++\c++核心编程\屏幕截图 2022-11-20 195935.png)]
访问父类同名成员需要加作用域
例
cout<<"父类 下的成员"<<s.父类名::m_A<<endl;
同名成员函数处理
图示
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-43R50qXj-1676032966229)(C:\Users\y’z\Desktop\C++\c++核心编程\屏幕截图 2022-11-20 200737.png)]
总结:
1.子类对象可以直接访问到子类中同名成员
2.子类对象加作用域可以访问到父类同名成员
3.当子类与父类拥有同名的成员函数,子类会隐藏父类中同名成员函数,加作用域可以访问到父类中同名函数
6,继承同名静态成员处理方式
静态成员和非静态成员出现同名,处理方式—致(只不过有两种访问方法)
(1)访问子类同名成员直接访问即可
(2)访问父类同名成员需要加作用域
访问静态成员可通过两种方式
图示
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-9Y0TGNkU-1676032966230)(C:\Users\y’z\Desktop\C++\c++核心编程\屏幕截图 2022-11-21 130235.png)]
访问静态成员函数也可通过两种方式
图示
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-oOO8xAAg-1676032966230)(C:\Users\y’z\Desktop\C++\c++核心编程\屏幕截图 2022-11-21 130939.png)]
7,多继承语法
C++允许一个类继承多个类
语法:class子类∶继承方式父类1,继承方式父类2…
多继承可能会引发父类中有同名成员出现,需要加作用域区分(即二义性)
C++实际开发中不建议用多继承
8,菱形继承
菱形继承概念:
两个派生类继承同一个基类
又有某个类同时继承者两个派生类
这种继承被称为菱形继承,或者钻石继承
图示
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-l2fsWRvl-1676032966230)(C:\Users\y’z\Desktop\C++\c++核心编程\屏幕截图 2022-11-21 192440.png)]
利用虚继承,可以菱形继承的问题
图示
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-xQhxOIl7-1676032966230)(C:\Users\y’z\Desktop\C++\c++核心编程\屏幕截图 2022-11-21 193539.png)]
底层图示
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-RNo3XbOu-1676032966231)(C:\Users\y’z\Desktop\C++\c++核心编程\屏幕截图 2022-11-21 194043.png)]
总结
(1)菱形继承带来的主要问题是子类继承两份相同的数据,导致资源浪费以及毫无意义
(2)利用虚继承可以解决菱形继承问题(即virtual关键字)
4,多态
1,多态的基本概念
多态是C++面向对象三大特性之一
多态分为两类
静态多态:函数重载和运算符重载属于静态多态,复用函数名()
动态多态:派生类和虚函数实现运行时多态
静态多态和动态多态区别:
静态多态的函数地址早绑定 - 编译阶段确定函数地址
动态多态的函数地址晚绑定 - 运行阶段确定函数地址
函数重写定义
重写函数返回值类型函数名参数列表完全相同
虚函数关键字(virtual)
class Person
{
public:
//虚函数
virtual void speak()
{
cout << "人在说话" << endl;
}
};
class xiaoming:public Person
{
public:
void speak()
{
cout << "小明在说话" << endl;
}
};
class xiaohong :public Person
{
public:
void speak()
{
cout << "小红在说话" << endl;
}
};
//执行说话的函数
//地址早绑定在编译阶段确定函数地址
//如果想执行让猫说话,那么这个函数地址就不能提前绑定
//需要在运行阶段进行绑定,地址晚绑定
//即使用虚函数
// 父类的指针或者引用执行子类对象
void doSpeak(Person &person)//Person &person=xm;
{
person.speak();
}
void test01()
{
xiaoming xm;
xiaohong xh;
doSpeak(xm);
doSpeak(xh);
}
int main()
{
test01();
return 0;
}
多态满足条件:
1、有继承关系
2、子类重写分类中的虚函数
多态使用:
父类指针或引用指向子类对象
指针类型字节大小为4个字节
图示
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-dAWjlfjk-1676032966231)(C:\Users\y’z\Desktop\C++\c++核心编程\屏幕截图 2022-11-21 215116.png)]
2,多态案例一计算器类
案例描述:
分别利用普通写法和多态技术,设计实现两个操作数进行运算的计算器类。
多态的优点:
代码组织结构清晰。
可读性强。
利于前期和后期的扩展以及维护。
例
//传统计算机方法
class Calculator
{
public:
int getRseult(string oper)
{
if (oper == "+")
{
return m_Num1 + m_Num2;
}
else if (oper == "-")
{
return m_Num1 - m_Num2;
}
else if (oper == "*")
{
return m_Num1 * m_Num2;
}
else if(oper=="/")
{
return m_Num1 / m_Num2;
}
}
int m_Num1;
int m_Num2;
};
//使用多态的方法
class AbstractCalculator
{//实现计算机抽象类即类内没有计算过程
public:
virtual int gtResult()
{
return 0;
}
int m_Num1;
int m_Num2;
};
class Add :public AbstractCalculator
{
int gtResult()
{
return m_Num1 + m_Num2;
}
};
class jian :public AbstractCalculator
{
int gtResult()
{
return m_Num1 - m_Num2;
}
};
class ceng :public AbstractCalculator
{
int gtResult()
{
return m_Num1 * m_Num2;
}
};
void test01()
{
Calculator c;
c.m_Num1 = 10;
c.m_Num2 = 10;
cout << c.m_Num1 << "*" << c.m_Num2 << "=" << c.getRseult("*") << endl;
}
void test02()
{ //多态使用条件
//父类指针或者引用指向子类对象
AbstractCalculator* abc = new ceng;//创建乘法计算器所以下面计算为乘法
abc->m_Num1 = 10;
abc->m_Num2 = 10;
cout << abc->m_Num1 << "*" << abc->m_Num2 << "=" << abc->gtResult() << endl;
delete abc//即使用完了删除数但是下面还是可以使用new指针只是没有数据
}
int main()
{
//test01();
test02();
return 0;
}
3,纯虚函数和抽象类
在多态中,通常父类中虚函教的实现是毫无意义的,主要都是调用子类重写的内容
因此可以将虚函数改为纯虚函数
纯虚函数语法:
virtual返回值类型函数名(参数列表)= 0 ;
当类中有了纯虚函数,这个类也称为抽象类
抽象类特点:
(1)无法实例化对象
(2)子类必须重写抽象类中的纯虚函数,否则也属于抽象类
图示
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Rw0zmkhk-1676032966231)(C:\Users\y’z\Desktop\C++\c++核心编程\屏幕截图 2022-11-22 131509.png)]
多态案例2,制作饮品
代码
class yinpin//纯虚函数
{
public:
virtual void Boil() = 0;
virtual void Brew() = 0;
virtual void PourInCup() = 0;
virtual void Putthing() = 0;
void makeyinpin()
{
Boil();
Brew();
PourInCup();
Putthing();
}
};
class kaf:public yinpin
{
virtual void Boil()
{
cout << "煮水" << endl;
}
virtual void Brew()
{
cout << "冲泡咖啡" << endl;
}
virtual void PourInCup()
{
cout << "倒入杯中" << endl;
}
virtual void Putthing()
{
cout << "加入糖和奶" << endl;
}
};
class caye:public yinpin
{
virtual void Boil()
{
cout << "煮水" << endl;
}
virtual void Brew()
{
cout << "冲泡茶叶" << endl;
}
virtual void PourInCup()
{
cout << "倒入杯中" << endl;
}
virtual void Putthing()
{
cout << "加入柠檬" << endl;
}
};
void doWork(yinpin* abc)//用指针来接收new的指针
{
abc->makeyinpin();
}
void test01()//我想的方法
{
kaf k;
k.makeyinpin();
caye c;
c.makeyinpin();
}
void test02()//老师想的方法
{
doWork(new kaf);//使用new返回一个堆区的指针要用对应的指针来接收
}
int main()
{
test01();
test02();
return 0;
}
5,虚析构和纯虚析构
多态使用时,如果子类中有属性开辟到堆区,那么父类指针在释放时无法调用到子类的析构代码
图示
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-bJU76ZCd-1676032966231)(C:\Users\y’z\Desktop\C++\c++核心编程\屏幕截图 2022-11-22 162307.png)]
解决方式:将父类中的析构函数改为虚析构或者纯虚析构
虚析构图示
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Anhfx1W7-1676032966231)(C:\Users\y’z\Desktop\C++\c++核心编程\屏幕截图 2022-11-22 162455.png)]
纯虚析构图示
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-4trFQr21-1676032966232)(C:\Users\y’z\Desktop\C++\c++核心编程\屏幕截图 2022-11-22 163223.png)]
虚析构和纯虚析构共性:
可以解决父类指针释放子类对象
都需要有具体的函数实现虚析构和纯虚析构
区别:
如果是纯虚析构,该类属于抽象类,无法实例化对象
虚析构语法:
virtual ~类名(){}
纯虚析构语法:
virtual ~类名()=0;
类名::~类名(){}
总结:
1.虚析构或纯虚析构就是用来解决通过父类指针释放子类对象。
2.如果子类中没有堆区数据,可以不写为虚析构或纯虚析构。
3.拥有纯虚析构函数的类也属于抽象类。
多态案例3,电脑组装
案例思路图示
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-T3Y5fEW5-1676032966232)(C:\Users\y’z\Desktop\C++\c++核心编程\屏幕截图 2022-11-22 165028.png)]
案例代码
//抽象CPU类
class CPU
{
public:
virtual void jisuan() = 0;
};
//抽象显卡类
class Xiank
{
public:
virtual void xians() = 0;
};
//抽象类
class Neic
{
public:
virtual void cengc() = 0;
};
class Computer
{
public:
Computer(CPU*cpu,Xiank*xk,Neic*nc)//将三个指针传入构造函数
{
m_cpu = cpu;//并用类内指针来接收
m_vc = xk;
m_mem = nc;
}
void zuz(CPU* cpu, Xiank* xk, Neic* nc)//将三个指针传入构造函数
{
m_cpu = cpu;//并用类内指针来接收
m_vc = xk;
m_mem = nc;
}
void work()
{
m_cpu->jisuan();//将上个函数传入的指针再传入个个分类中
m_vc->xians();
m_mem->cengc();
}
//提供析构函数来释放三个电脑零件
~Computer()
{
if (m_cpu != NULL)
{
delete m_cpu;
m_cpu = NULL;
}
if (m_vc != NULL)
{
delete m_vc;
m_vc = NULL;
}
if (m_mem != NULL)
{
delete m_mem;
m_mem = NULL;
}
}
private:
CPU* m_cpu;//CPU指针
Xiank* m_vc;//显卡指针
Neic* m_mem;//内存指针
};
//Intel厂商
class IntelCpu:public CPU
{
void jisuan()
{
cout << "Intel的CPU开始计算了"<<endl;
}
};
class IntelXiank :public Xiank
{
void xians()
{
cout << "Intel的显卡开始显示了" << endl;
}
};
class IntelNeic :public Neic
{
void cengc()
{
cout << "Intel的内存开始存储了" << endl;
}
};
//Lenovo厂商
class LenovoCpu :public CPU
{
void jisuan()
{
cout << "Lenovo的CPU开始计算了" << endl;
}
};
class LenovoXiank :public Xiank
{
void xians()
{
cout << "Lenovo的显卡开始显示了" << endl;
}
};
class LenovoNeic :public Neic
{
void cengc()
{
cout << "Lenovo的内存开始存储了" << endl;
}
};
void test01()
{
CPU* intelCpu = new IntelCpu;
Xiank* intelxiank = new IntelXiank;
Neic* intelneic = new IntelNeic;
//创建第一台电脑
cout << "第一台电脑" << endl;
Computer* computer1 = new Computer(intelCpu,intelxiank, intelneic);
computer1->work();
delete computer1;
//创建第二台电脑
cout << "第二台电脑" << endl;
Computer* computer2 = new Computer(new LenovoCpu, new LenovoXiank, new LenovoNeic);
computer2->work();
delete computer2;
cout << "第三台电脑" << endl;
Computer* computer3 = new Computer(new LenovoCpu, new IntelXiank, new LenovoNeic);
computer3->work();
delete computer3;
}
int main()
{
test01();
return 0;
}
五,文件操作
程序运行时产生的数据都属于临时数据,程序一旦运行结束都会被释放
通过文件可以将数据持久化
C++中对文件操作需要包含头文件**< fstream >**
文件类型分为两种:
1.文本文件 - 文件以文本的ASCII码形式存储在计算机中
2.**二进制文件 ** - 文件以文本的二进制形式存储在计算机中,用户一般不能直接读懂它们
操作文件的三大类
1.ofstream:写操作
2.fstream :读写操作
3.fstream :读写操作
1,文本文件
1,写文件写文件
步骤如下:
1.包含头文件
#include
2.创建流对象
ofstream ofs;
3.打开文件
ofs.open(“文件路径”.,打开方式);
4.写数据
ofs <<“写入的数据”";
5.关闭文件
ofs.close();
文件打开方式图示
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-LQ8Eti6m-1676032966232)(C:\Users\y’z\Desktop\C++\c++核心编程\屏幕截图 2022-11-22 190358.png)]
注意:文件打开方式可以配合使用,利用|操作符
例如:用二进制方式写文件
ios::binary |ios::out
代码案例
#define _CRT_SECURE_NO_WARNINGS 1
#include<iostream>
#include<string>
#include<math.h>
#include<fstream>
using namespace std;
//创建流对象
void test01()
{//创建流对象
ofstream ofs;
//指定打开方式
ofs.open("test.txt", ios::out);
//写内容
ofs << "姓名:张三" << endl;
ofs << "性别:男" << endl;
ofs << "年龄:18" << endl;
//关闭文件
ofs.close();
//创建流对象
ifstream ifs;
//打开文件并且判断是否打开成功
ifs.open("test.txt",ios::in);
if (!ifs.is_open())
{
cout << "文件打开失败" << endl;
return;
}
//读数据
//第一种
//char buf[1024] = { 0 };
//while (ifs >> buf)
//{
// cout << buf << endl;
//}
//第二种
//char buf[1024] = { 0 };
//while (ifs.getline(buf, sizeof(buf)))
//{
// cout << buf << endl;
//}
//第三种
string buf;
while (getline(ifs, buf))
{
cout << buf << endl;
}
//第四种
//char c;
//while ((c = ifs.get()) != EOF)//EOF endl of file
//{
//cout << c;
//}
//关闭文件
ifs.close();
}
总结:
文件操作必须包含头文件fstream
读文件可以利用ofstream ,或者fstream类
打开文件时候需要指定操作文件的路径,以及打开方式
利用<<可以向文件中写数据
操作完毕,要关闭文件
2,二进制文件
以二进制的方式对文件进行读写操作
打开方式要指定为ios:binary
1,写文件
二进制方式写文件主要利用流对象调用成员函数write
函数原型:
ostream& write(const char * buffer,int len);
参数解释:字符指针buffer指向内存中一段存储空间。len是读写的字节数
二进制写文件
图示
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-xSC4LafA-1676032966232)(C:\Users\y’z\Desktop\C++\c++核心编程\屏幕截图 2022-11-22 200134.png)]
2,读文件
二进制方式读文件主要利用流对象调用成员函数read
函数原型:
istream& read(char *buffer,int len);
参数解释:字符指针buffer指向内存中一段存储空间。len是读写的字节数示例:
图示
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-1mKYkJxR-1676032966233)(C:\Users\y’z\Desktop\C++\c++核心编程\屏幕截图 2022-11-22 201057.png)]
职工管理系统
第一部分,职工管理系统主文件即菜单
利用while先做死循环,在用switch进行分支选择
#define _CRT_SECURE_NO_WARNINGS 1
#include<iostream>
#include<string>
#include"workerManager.h"
using namespace std;
int main()
{
//实例化对象
WorkerManager wm;
//调用展示菜单成员函数
int cus = 0;//存储用户选项
while (true)//死循环可以无限选择
{
wm.Show_Menu();
cout << "请输入你的选择" << endl;
cin >> cus;
switch (cus)
{
case 0:wm.ExitSystem();//退出系统
break;
case 1:wm.Add_Emp();//添加职工
break;
case 2: wm.Show_Emp();//显示职工
break;
case 3:wm.Del_Emp();//删除职工
break;
case 4:wm.Mod_Emp();//修改职工
break;
case 5:wm.Find_Emp();//查找职工
break;
case 6:wm.Sort_Emp();//编号排序
break;
case 7:wm.Clean_File();//清空文档
break;
default:
system("cls");//清空屏幕
break;
}
}
system("pause");
return 0;
}
第二部分,职工管理类(主要部分)
先创建一个workerManager.h头文件内只放函数声明并在workerManager.cpp做实现
#define _CRT_SECURE_NO_WARNINGS 1
#pragma once//防止头文件重复包含
#include<iostream>//包含输入输出流的文件
#include<fstream>
#define FILENAME "empFile.txt"//宏常量定义文件名
#include"worker.h"
#include"employee.h"
#include"manager.h"
#include"boss.h"
using namespace std;//使用标准命名空间
//菜单抽象类
class WorkerManager
{
public:
//构造函数
WorkerManager();
//展示函数
void Show_Menu();
//退出系统
void ExitSystem();
//保存文件
void save();
//添加职工函数
void Add_Emp();
//判断文件是否为空的标志
bool m_FileIsEmpty;
//统计人数
int getp_EmpNum();
//初始化职工
void init_Emp();
//显示职工
void Show_Emp();
//删除职工
void Del_Emp();
//查找职工是否存在通过编号来查找如果存在返回在数组中的位置
//不存在返回-1
int IsExist(int id);
//显示修改例表
void xiugai2();
//修改职工
void Mod_Emp();
//查找职工
void Find_Emp();
//排序职工
void Sort_Emp();
//清空职工
void Clean_File();
//析构函数
~WorkerManager();
//记录职工人数
int m_EmpNum;
// 职工数组指针
Worker** m_EmpArray;//一个Worker类的空间内有类成员三个属性
//**二级指针可以存放其他指针的地址
};
第三部分,创建职工类及旗下类
创建职工类(worker.h)并在其下方创建三个子类(manager.h,employee.h,boos.h)
worker.h抽象类(只放函数定义)
#pragma once
#include<iostream>
#include<string>
using namespace std;
//职工抽象类
class Worker
{
public:
//显示个人信息
virtual void showInfo() = 0;//纯虚函数
//获取岗位名称
virtual string getDeptName() = 0;
int m_Id;//成员编号
string m_Name;//职工姓名
int m_DeptIdl;//职工所在部门名称编号
};
manager.h抽象类及旗下manager.cpp
#pragma once
#include<iostream>
using namespace std;
#include"worker.h"
class Manager :public Worker
{
public:
//构造函数因每个经理在创造时应该初始化属性
Manager(int id, string name, int did);
virtual void showInfo();//展示信息函数
//virtual关键字可以删除可以不删
virtual string getDeptName();//获得部门函数
};
manager.cpp(函数实现)
#define _CRT_SECURE_NO_WARNINGS 1
#include"manager.h"
Manager::Manager(int id, string name, int did)
{
this->m_Id=id;
this->m_Name=name;
this->m_DeptIdl=did;
}
void Manager::showInfo()
{
cout << "职工编号:" << this->m_Id
<< "\t职工姓名" << this->m_Name
<< "\t岗位" << this->getDeptName()
<< "\t岗位职责:完成老板提交的任务,并下发任务给员工" << endl;
}
string Manager::getDeptName()
{
return"经理";//return "经理"==return string("经理")
//如果不写string则系统自带即帮我们隐实现
}
employee.h抽象类及旗下employee.cpp
#pragma once
//普通职工文件
//只放函数定义
#include<iostream>
using namespace std;
#include"worker.h";
class Employee :public Worker
{
public:
//构造函数因每个员工在创造时应该初始化属性
Employee(int id, string name, int dId);
//显示个人信息
virtual void showInfo();//virtual关键字可以删除可以不删
//获取岗位名称
virtual string getDeptName();
};
employee.cpp(函数实现)
#define _CRT_SECURE_NO_WARNINGS 1
#include"employee.h"
//构造函数
//只放函数实现所以不用写类
Employee::Employee(int id, string name, int dId)
{
this->m_Id = id;
this->m_Name = name;
this->m_DeptIdl = dId;
}
//显示个人信息
void Employee::showInfo()
{
cout << "职工编号:" << this->m_Id
<< "\t职工姓名" << this->m_Name
<<"\t岗位"<<this->getDeptName()
<<"\t岗位职责:完成经理提交的任务"<<endl;
}
//获取岗位名称
string Employee::getDeptName()
{
return "员工";
}
boss.h抽象类及旗下boss.cpp
#pragma once
#include<iostream>
using namespace std;
#include"worker.h"
class Boss :public Worker
{
public:
//构造函数因每个老板在创造时应该初始化属性
Boss(int id, string name, int dId);
//显示个人信息
virtual void showInfo();
//获取岗位名称
virtual string getDeptName();
};
boss.cpp(函数实现)
#define _CRT_SECURE_NO_WARNINGS 1
#include"boss.h"
//只放函数实现
Boss::Boss(int id, string name, int dId)
{
this->m_Id = id;
this->m_Name = name;
this->m_DeptIdl = dId;
}
void Boss::showInfo()
{
cout << "职工编号:" << this->m_Id
<< "\t职工姓名" << this->m_Name
<< "\t岗位" << this->getDeptName()
<< "\t岗位职责:管理公司大小事务" << endl;
}
string Boss::getDeptName()
{
return "老板";//return "老板"==return string("老板")
//如果不写string则系统自带即帮我们隐实现
}
第四部分,构造函数和析构函数
因函数定义已经在workerManager.h头文件内故只展示函数实现部分
构造函数
WorkerManager::WorkerManager()
{//判断三种情况
//1.文件不存在
ifstream ifs;
ifs.open(FILENAME, ios::in);
if (!ifs.is_open())
{
cout << "文件不存在" << endl;
//初始化属性
//初始化记录人数
this->m_EmpNum = 0;
//初始化数组指针
this->m_EmpArray = NULL;
//初始化文件是否为空
this->m_FileIsEmpty = true;//布尔类型的数据故只有1和0,即真假
ifs.close();
return;
}
//2,文件存在,并且没有记录
char ch;
ifs >> ch;//读取一个字符如果读取到的为文件尾则读走最后一个使文件为空
if (ifs.eof())//eof()判断文件是否为空
{
cout << "文件为空" << endl;
this->m_EmpNum = 0;
this->m_FileIsEmpty = true;
this->m_EmpArray = NULL;
ifs.close();
return;
}
//3.文件存在并且记录了数据
int num = this->getp_EmpNum();
cout << "职工人数为" << num << endl;
this->m_EmpNum = num;
//开辟空间
this->m_EmpArray = new Worker * [this->m_EmpNum];
//将文件中的数据存放进数组中即this指针指向最近的对象即
//上行new出来的数组
this->init_Emp();
}
析构函数
WorkerManager::~WorkerManager()
{
if (this->m_EmpArray != NULL)//判断数组指针是否为空
{
for (int i = 0; i < this->m_EmpNum; i++)//利用循环清空数组数据释放堆区内存
{
if (this->m_EmpArray[i] != NULL)
{
delete this->m_EmpArray[i];
}
}
delete[]this->m_EmpArray;
this->m_EmpArray = NULL;
}
}
第五部分,展示菜单函数和退出函数
因函数定义已经在workerManager.h头文件内故只展示函数实现部分
展示菜单函数
void WorkerManager::Show_Menu()//标明定义域是在workerManager.h头文件中的WorkerManager类中
{
cout << "************************************" << endl;
cout << "********欢迎使用员工管理系统********" << endl;
cout << "***********0.退出管理系统***********" << endl;
cout << "***********1.添加职工信息***********" << endl;
cout << "***********2.显示职工信息***********" << endl;
cout << "***********3.删除职工信息***********" << endl;
cout << "***********4.修改职工信息***********" << endl;
cout << "***********5.查找职工信息***********" << endl;
cout << "***********6.按照编号排序***********" << endl;
cout << "***********7.清空所有文档***********" << endl;
cout << "************************************" << endl;
cout << endl;
}
退出函数
void WorkerManager::ExitSystem()
{
cout << "欢迎下次使用" << endl;
system("pause");
exit(0);//不管那一个代码中只要按零调用程序即退出
}
第六部分,添加职工
因函数定义已经在workerManager.h头文件内故只展示函数实现部分
cout << "请输入添加的职工数量" << endl;
int addNum = 0;//保存用户输入数量
cin >> addNum;
if(addNum>0)//添加
{
//计算添加新的空间大小
int newSize = this->m_EmpNum + addNum;
//开辟新空间//创建在worker类的新对象newSpace
//并将堆区的新建了堆区数组
Worker** newSpace = new Worker * [newSize];
//将原来空间下的数据,拷贝到新空间下
if (this->m_EmpArray != NULL)
{
for (int i = 0; i < this->m_EmpNum; i++)
{
/* newSpace = this->m_EmpNum + newSpace;*/
newSpace[i] = this->m_EmpArray[i];
}
}
//批量添加新数据
for (int i = 0; i < addNum; i++)
{
int id;
string name;
int dSelect;//部门选择
cout << "请输入第" << i + 1 << "个职工编号为" << endl;
cin >> id;
int a =IsExist(id);
if (a!=-1)//后添加代码判断职工编号是否重复
{
cout << "输入职工编号错误,请重新输入添加" << endl;
system("pause");
system("cls");
return;
}
cout << "请输入第" << i + 1 << "个职工姓名为" << endl;
cin >> name;
cout << "请选择该职工部门为" << endl;
cout << "1.普通职工" << endl;
cout << "2.经理" << endl;
cout << "3.老板" << endl;
cin >> dSelect;//记录部门选择
Worker* worker = NULL;//创建worker对象并为空
switch (dSelect)//利用switch选择不同的部门进入不同的类
{
case 1:
worker = new Employee(id, name, 1);
break;
case 2:
worker = new Manager(id, name, 2);
break;
case 3:
worker = new Boss(id, name, 3);
break;
}
//将创建职工职责,保存到数组中
newSpace[(this->m_EmpNum) + i] = worker;
}
delete[] this->m_EmpArray;//释放数组要加中括号
//更改新空间指向
this->m_EmpArray = newSpace;
//更新新的职工人数
this->m_EmpNum = newSize;
//更新职工不为空的情况
this->m_FileIsEmpty = false;
//提示添加成功
cout << "添加成功" << addNum << "名新职工" << endl;
//保存到文件中
this->save();
}
else
{
cout << "输入错误" << endl;
}
system("pause");
system("cls");
}
第七部分,显示职工
因函数定义已经在workerManager.h头文件内故只展示函数实现部分
void WorkerManager::Show_Emp()
{
if (this->m_FileIsEmpty==true)//即文件为true即为真则不存在
{
cout << "文件不存在或记录为空" << endl;
}
else
{
for (int i = 0; i < m_EmpNum; i++)
{
//利用多态调用接口
this->m_EmpArray[i]->showInfo();//即进入worker.h中的worker.cpp再进入旗下三类中
}
}
system("pause");
system("cls");
}
第八部分,查找职工和删除职工
因函数定义已经在workerManager.h头文件内故只展示函数实现部分
查找职工函数
int WorkerManager::IsExist(int id)//查找函数
{//但是只能查找一个同编号的人
int index = -1;//
for (int i = 0; i < this->m_EmpNum; i++)
{
if (this->m_EmpArray[i]->m_Id == id)//找到职工
{
index = i;
break;
}
}
return index;
}
删除职工函数
void WorkerManager::Del_Emp()
{
if (this->m_FileIsEmpty)
{
cout << "文件不存在或记录为空" << endl;
}
else
{
cout << "请输入想要删除职工编号" << endl;
int id = 0;
cin >> id;
int index=this->IsExist(id);
if (index == -1)
{
cout << "查无此人" << endl;
}
else
{
for (int i = index; i < this->m_EmpNum-1; i++)//用覆盖的方法来删除数据
{
//数据前移
this->m_EmpArray[i] = this->m_EmpArray[i + 1];
}
this->m_EmpNum--;//更新数组记录人数
this->save();//删除后数据同步到文件中
cout << "删除成功" << endl;
}
}
system("pause");
system("cls");
}
第九部分,修改职工
因函数定义已经在workerManager.h头文件内故只展示函数实现部分
void WorkerManager::Mod_Emp()
{
if (this->m_FileIsEmpty)//判断文件是否存在
{
cout << "文件不存在或记录为空" << endl;
}
else
{
cout << "请输入想要修改职工编号" << endl;
int id = 0;
cin >> id;
int index = this->IsExist(id);
if (index == -1)//没有找到职工
{
cout << "查无此人" << endl;
}
else
{
delete this->m_EmpArray[index];//因为不知道要修改该职工哪个属性故直接删除该职工重新创建
int newId = 0;
string newName;
int dSelect = 0;
//在做一次添加操作
cout << "请输入修改后的编号" << endl;
cin >> newId;
cout << "请输入修改后的姓名" << endl;
cin >> newName;
cout << "请输入修改后的岗位" << endl;
cout << "1.普通职工" << endl;
cout << "2.经理" << endl;
cout << "3.老板" << endl;
cin >> dSelect;
Worker* worker = NULL;//创建一个新的类将上面数据利用switch放入不同的子类
switch (dSelect)
{
case 1:
worker = new Employee(newId, newName,dSelect );
break;
case 2:
worker = new Manager(newId, newName, dSelect);
break;
case 3:
worker = new Boss(newId, newName, dSelect);
break;
default:
break;
}
this->m_EmpArray[index] = worker;//将新类直接赋值到原来的位置
cout << "修改成功" << this->m_EmpArray[index]->m_DeptIdl
<< endl;
this->save();//写入文件
}
system("pause");
system("cls");
}
}
第十部分,查找职工
因函数定义已经在workerManager.h头文件内故只展示函数实现部分
void WorkerManager::Find_Emp()
{
if (this->m_FileIsEmpty)
{
cout << "文件不存在或记录为空" << endl;
}
else
{
cout << "请输入查找的方式" << endl;
cout << "1,按照职工编号查找" << endl;
cout << "2,按姓名查找" << endl;
}
int select = 0;
cin >> select;
if (select == 1)//可以使用函数重载
{
int id;
cout << "请输入的职工编号" << endl;
cin >> id;
int ret = IsExist(id);//因查编号函数故只能查第一个同编号的人
if(ret!=-1)
{
cout << "查找成功,该职工信息如下" << endl;
this->m_EmpArray[ret]->showInfo();
}
else
{
cout << "查找失败,查无此人" << endl;
}
}
else if (select == 2)
{
string name;
cout << "请输入查找的姓名" << endl;
cin >> name;
bool flag = false;//查找到的标志
for (int i = 0; i < m_EmpNum; i++)//遍历数组来查职工故可以查出多名同姓名的职工
{
if (m_EmpArray[i]->m_Name == name)
{
cout << "查找成功,职工编号为"
<< m_EmpArray[i]->m_Id
<< "号的信息如下:" << endl;
flag = true;//因无法在循环中无法用else判断是否查到故增加一个bool类型
this->m_EmpArray[i]->showInfo();
}
}
if (flag == false)
{
cout << "查找失败,查无此人" << endl;
}
}
else
{
cout << "输入选项错误" << endl;
}
system("pause");
system("cls");
}
第十一部分,对职工编号排序
因函数定义已经在workerManager.h头文件内故只展示函数实现部分
void WorkerManager::Sort_Emp()
{
if (this->m_FileIsEmpty)
{
cout << "文件不存在或记录为空" << endl;
system("pause");
system("cls");
return;
}
else
{
cout << "请选择排序方法" << endl;
cout << "1.按职工编号进行升序" << endl;
cout << "2.按职工编号进行降序" << endl;
}
int select;
cin >> select;
//**选择排序**
//for (int i = 0; i < m_EmpNum; i++)//选择排序遍历数组
//{
// int minOrMax = i;//最小或最大
// for (int j = i + 1; j < m_EmpNum; j++)
// {
// if (select == 1)//升序即从小到大
// {
// if (m_EmpArray[minOrMax]->m_Id > m_EmpArray[j]->m_Id)
// {
// minOrMax = j;
// }
// }
// else if(select == 2)//降序即从大到小
// {
// if (m_EmpArray[minOrMax]->m_Id < m_EmpArray[j]->m_Id)
// {
// minOrMax = j;
// }
// }
// else
// {
// cout << "输入错误" << endl;
// system("pause");
// system("cls");
// return;
// }
// if (i != minOrMax)//交换位置
// {
// Worker* temp = m_EmpArray[i];
// m_EmpArray[i] = m_EmpArray[minOrMax];
// m_EmpArray[minOrMax] = temp;
// }
// }
//
//
//}
//cout << "排序成功,排序后结果为" << endl;
//this->save();
//this->Show_Emp();
if (select == 1)//冒泡排序
{
for (int i = 0; i < this->m_EmpNum-1; i++)//升序即从小到大
{
for (int j = 0; j < this->m_EmpNum - i - 1; j++)
{
if (m_EmpArray[j]->m_Id > m_EmpArray[j+1]->m_Id)
{
Worker* temp = m_EmpArray[j+1];
m_EmpArray[j+1] = m_EmpArray[j];
m_EmpArray[j] = temp;
}
}
}
cout << "排序成功,排序后结果为" << endl;
this->save();
this->Show_Emp();
}
else if(select == 2)
{
for (int i = 0; i < this->m_EmpNum - 1; i++)//降序即从大到小
{
for (int j = 0; j < this->m_EmpNum - i - 1; j++)
{
if (m_EmpArray[j]->m_Id < m_EmpArray[j + 1]->m_Id)
{
Worker* temp = m_EmpArray[j + 1];
m_EmpArray[j + 1] = m_EmpArray[j];
m_EmpArray[j] = temp;
}
}
}
cout << "排序成功,排序后结果为" << endl;
this->save();
this->Show_Emp();//因为Show_Emp中已经存在清屏操作
//故下方不用清屏
}
else
{
cout << "输入错误" << endl;
system("pause");
system("cls");
return;
}
//system("pause");因上方Show_Emp()函数已经有清屏操作
//system("cls");
}
第十二部分,清空文档
因函数定义已经在workerManager.h头文件内故只展示函数实现部分
void WorkerManager::Clean_File()
{
cout << "确认是否清空" << endl;
cout << "1,确认" << endl;
cout << "2,返回" << endl;
int select = 0;
cin >> select;
if (select == 1)
{
ofstream ofs(FILENAME, ios::trunc);//删除文件在创建的操作
ofs.close();
if (this->m_EmpArray != NULL)//先判断指针内是否为空
{
for (int i = 0; i < this->m_EmpNum; i++)//利用循环将每一个指针内数组数据删除
{
if (this->m_EmpArray[i] != NULL)
{
delete this->m_EmpArray[i];
}
}
this->m_EmpNum = 0;
delete[]this->m_EmpArray;//再删除m_EmpArray指针数组删除数组要用delete[]
this->m_EmpArray = NULL;
this->m_FileIsEmpty = true;//再将文件设为不存在
}
cout << "清空成功" << endl;
}
system("pause");
system("cls");
}
第十三部分,文件交互读写(外加)
因函数定义已经在workerManager.h头文件内故只展示函数实现部分
写文件函数
void WorkerManager::save()//写文件函数
{
ofstream ofs;//创建流对象
ofs.open(FILENAME, ios::out);//打开文件
for (int i = 0; i < this->m_EmpNum; i++)//利用循环将数据写入文件
{
ofs << this->m_EmpArray[i]->m_Id << " "
<< this->m_EmpArray[i]->m_Name << " "
<< this->m_EmpArray[i]->m_DeptIdl << endl;
}
ofs.close();//关闭系统
}
读文件函数1
void WorkerManager::init_Emp()//利用读文件函数可以将文件内的数据读回运行框
{
ifstream ifs;
ifs.open(FILENAME, ios::in);//iOS::in只读
int id;
string name;
int dId;
int index = 0;
while (ifs >> id && ifs >> name && ifs >> dId)//利用while循环来读取
{
Worker* worker = NULL;//创建一个worker类指针并为空
//根据不同的部门Id创建不同对象
if (dId == 1)//普通员工
{
worker = new Employee(id, name, dId);
}
else if (dId == 2)//2经理
{
worker = new Manager(id, name, dId);
}
else//老板
{
worker = new Boss(id, name, dId);
}
this->m_EmpArray[index] = worker;//再将worker类数据赋值给指针数组
index++;
}
}
读文件函数2
int WorkerManager::getp_EmpNum()//统计文件人数
{
ifstream ifs;
ifs.open(FILENAME, ios::in);
int id;
string name;
int dId;
int num = 0;
while (ifs >> id && ifs >> name && ifs >> dId)
{
num++;//统计人数
}
ifs.close();
return num;
}
C++提高编程
本阶段主要针对C++泛型编程和ST[技术做详细讲解,探讨C++更深层的使用
1,模板
1,模板的概念
模板就是建立通用的模具,大大提高复用性
例如生活中的模板
—寸照片模板
PPT模板
2,函数模板
C++另一种编程思想称为泛型编程,主要利用的技术就是模板。
C++提供两种模板机制:函数模板和类模板
1,函数模板语法
函数模板作用:
建立一个通用函数,其函数返回值类型和形参类型可以不具体制定,用一个虚拟的类型来代表。
语法:
template<typename T>
//函数声明或定义
解释:
template —声明创建模板
typename —表面其后面的符号是一种数据类型,可以用class代替
T —通用的数据类型,名称可以替换,通常为大写字母
代码案例
#define _CRT_SECURE_NO_WARNINGS 1
#include<iostream>
#include<string>
using namespace std;
template<typename T>
void mySwap(T&a,T&b)
{
T temp = a;
a = b;
b = temp;
}
int main()
{
int a = 10;
int b = 20;
//swapInt(a, b);
//利用函数模板交换
//两种方式使用函数模板
//1.自动类型推导
//mySwap(a,b);
//2、显示指定类型
mySwap<int>(a,b);
cout << "a=" << a << endl;
cout << "b=" << b << endl;
return 0;
}
总结:
1,函数模板利用关键字template
2,使用函数模板有两种方式:自动类型推导、显示指定类型
3,模板的目的是为了提高复用性,将类型参数化
2,函数模板注意事项
注意事项:
1,自动类型推导,必须推导出一致的数据类型T,才可以使用
图示
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-GWqBFfjl-1676032966234)(C:\Users\y’z\Desktop\C++\c++核心编程\屏幕截图 2022-11-25 191855.png)]
2,模板必须要确定出T的数据类型,才可以使用
错误图示
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-P8DEuohm-1676032966234)(C:\Users\y’z\Desktop\C++\c++核心编程\屏幕截图 2022-11-25 192157.png)]
正确图示
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-83ysL1jR-1676032966234)(C:\Users\y’z\Desktop\C++\c++核心编程\屏幕截图 2022-11-25 192139.png)]
3,函数模板案例
案例描述:
利用函数模板封装一个排序的函数,可以对不同数据类型数组进行排序
排序规则从大到小,排序算法为选择排序
分别利用char数组和int数组进行测试
代码案例
template<typename T>
void mySway(T& a, T& b)//交换元素
{
T temp = a;
a = b;
b = temp;
}
template<typename T>
void mySort(T a[])//排序数组
{
for (int i = 0; i < 10; i++)
{
int max = i;
for (int j = 1 + i; j < 10; j++)
{
if (a[max] < a[j])
max= j;
}
if (i != max)
{
mySway(a[i], a[max]);
}
}
}
template<typename T>
void printArray(T arr[])//打印数组
{
for (int i = 0; i < 10; i++)
{
cout << arr[i]<<" ";
}
cout << endl;
}
void test01()
{
int arr[10]={2,3,1,4,5,6,0,8,9,7};
mySort(arr);
printArray(arr);
}
void test02()
{
char arr[] = "jhgfsaa";
mySort(arr);
printArray(arr);
}
int main()
{
//test01();
test02();
return 0;
}
4,普通函数与函数模板的区别
普通函数与函数模板区别:
1,普通函数调用时可以发生自动类型转换(隐式类型转换)。
2,函数模板调用时,如果利用自动类型推导,不会发生隐式类型转换。
3,如果利用显示指定类型的方式,可以发生隐式类型转换。
普通函数图示
即因为普通函数形参中标注了传入类型故传入char系统会自动转化为int
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-MRpvzoIS-1676032966235)(C:\Users\y’z\Desktop\C++\c++核心编程\屏幕截图 2022-11-25 201436.png)]
函数模板图示
函数模板则要在前标注函数名<参数类型>(参数)否则报错
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-70bijrpy-1676032966235)(C:\Users\y’z\Desktop\C++\c++核心编程\屏幕截图 2022-11-25 201650.png)]
总结:建议使用显示指定类型的方式,调用函数模板,因为可以自己确定通用类型T。
5,普通函数与函数模板的调用规则
调用规则如下:
1,如果函数模板和普通函数都可以实现,优先调用普通函数
2,可以通过空模板参数列表来强制调用函数模板
图示
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-TbxBE1fb-1676032966235)(C:\Users\y’z\Desktop\C++\c++核心编程\屏幕截图 2022-11-25 210519.png)]
3,函数模板也可以发生重载
图示
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-FyfvpOA6-1676032966235)(C:\Users\y’z\Desktop\C++\c++核心编程\屏幕截图 2022-11-25 210656.png)]
4,如果函数模板可以产生更好的匹配,优先调用函数模板
即虽然普通函数可以进行隐式转化但是不如用函数模板
图示
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-bLeLhBos-1676032966235)(C:\Users\y’z\Desktop\C++\c++核心编程\屏幕截图 2022-11-25 210919.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-qMrUfpGg-1676032966236)(C:\Users\y’z\Desktop\C++\c++核心编程\屏幕截图 2022-11-25 210948.png)]
**总结:**既然提供了函数模板,最好就不要提供普通函数,否则容易出现二义性。
6,模板的局限性
局限性:
模板的通用性并不是万能的
例如
template<class T>
void f(T a, T b)
{
a = b;
}
在上述代码中提供的赋值操作,如果传入的a和b是一个数组,就无法实现了
例如
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-DiBYtRLn-1676032966236)(C:\Users\y’z\Desktop\C++\c++核心编程\屏幕截图 2022-11-25 211756.png)]
例
如果T的数据类型传入的是像Person这样的自定义数据类型,也无法正常运行
因此C++为了解决这种问题,提供模板的重载,可以为这些特定的类型提供具体化的模板
class Person
{
public:
Person(string name, int age)
{
this->m_Name = name;
this->m_Age = age;
}
//bool operator==(Person& p)//局部函数重载
//{
// if (this->m_Name == p.m_Name && this->m_Age == p.m_Age)//即如果我自身的m_Name==传进来的p.m_Name
// {
// return true;
// };
// return false;
//}
string m_Name;
int m_Age;
};
template<typename T>
bool fz(T &a,T &b)
{
return a == b;
}
//利用具体化Person的版本实现代码,具体化比函数重载优先调用
template<> bool fz(Person& p1, Person& p2)
{
if (p1.m_Name==p2.m_Name &&p1.m_Age==p2.m_Age)
{
return true;
};
return false;
}
void test01()
{
Person a("tom", 10);
Person b("tom", 10);
bool ret=fz(a, b);
if (ret)
{
cout << "a==b" << endl;
}
else
{
cout << "a!=b" << endl;
}
}
int main()
{
test01();
return 0;
}
总结:
1,利用具体化的模板,可以解决自定义类型的通用化
2,学习模板并不是为了写模板,而是在STL能够运用系统提供的模板
3,类模板
1,类模板语法
类模板作用:
建立一个通用类,类中的成员数据类型可以不具体制定,用一个虚拟的类型来代表。
语法:
1 template<typename T>
2 //类
解释:
template .–声明创建模板
typename —表面其后面的符号是一种数据类型,可以用class代替
T—通用的数据类型,名称可以替换,通常为大写字母
例
template<class NameType,class AgeType>//因为要创建对象有二个类成员故创建二个不同的数据类型
class Person
{
public:
Person(NameType name, AgeType age)
{
this->m_Name = name;
this->m_Age = age;
}
void Show_xinx()
{
cout << "姓名" << this->m_Name << "年龄" << m_Age << endl;
}
NameType m_Name;
AgeType m_Age;
};
void test01()
{
Person<string,int> p1 ("孙悟空",999);
p1.Show_xinx();
}
int main()
{
test01();
return 0;
}
**总结:**类模板和函数模板语法相似,I在声明模板template后面加类,此类称为类模板
2,类模板与函数模板区别
类模板与函数模板区别主要有两点:
1.类模板没有自动类型推导
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-BSpCkJGK-1676032966236)(C:\Users\y’z\Desktop\C++\c++核心编程\屏幕截图 2022-11-26 105931.png)]
2.类模板在模板参数列表中可以有默认参数**(函数模板不可以使用)**
template<class NameType,class AgeType=int>//即在模板函数中令AgeType=int类型则下方创建对象是就不用在<>内写AgeType类型
class Person
{
public:
Person(NameType name, AgeType age)
{
this->m_Name = name;
this->m_Age = age;
}
void Show_xinx()
{
cout << "姓名" << this->m_Name << "年龄" << m_Age << endl;
}
NameType m_Name;
AgeType m_Age;
};
void test01()
{
Person<string> p1 ("孙悟空",999);
p1.Show_xinx();
}
int main()
{
test01();
return 0;
}
3,类模板中成员函数创建时机
类模板中成员函数和普通类中成员函数创建时机是有区别的:
(1)普通类中的成员函数一开始就可以创建
(2)类模板中的成员函数在调用时才创建要确定数据类型才能调用
例
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-hLJ1XDof-1676032966236)(C:\Users\y’z\Desktop\C++\c++核心编程\屏幕截图 2022-11-26 111243.png)]
4,类模板对象做函数参数
学习目标:
类模板实例化出的对象,向函数传参的方式
一共有三种传入方式:
1.指定传入的类型—直接显示对象的数据类型
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-zotLL2Z8-1676032966237)(C:\Users\y’z\Desktop\C++\c++核心编程\屏幕截图 2022-11-26 111934.png)]
2.参数模板化—将对象中的参数变为模板进行传递(小知识可通过下方方法来查看T即通用的数据类型的类型为什么)
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-edvmjC3u-1676032966237)(C:\Users\y’z\Desktop\C++\c++核心编程\屏幕截图 2022-11-26 112553.png)]
3.整个类模板化—将这个对象类型模板化进行传递
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-IDkCNpaf-1676032966237)(C:\Users\y’z\Desktop\C++\c++核心编程\屏幕截图 2022-11-26 113015.png)]
总结:
(1)通过类模板创建的对象,可以有三种方式向函数中进行传参
(2)使用比较广泛是第一种:指定传入的类型
5,类模板与继承
当类模板碰到继承时,需要注意一下几点:
(1)当子类继承的父类是一个类模板时,子类在声明的时候,要指定出父类中的类型
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-0ba1BHQb-1676032966237)(C:\Users\y’z\Desktop\C++\c++核心编程\屏幕截图 2022-11-26 114058.png)]
(2)如果不指定,编译器无法给子类分配内存
(3)如果想灵活指定出父类中T的类型,子类也需变为类模板
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-whbwb0O5-1676032966237)(C:\Users\y’z\Desktop\C++\c++核心编程\屏幕截图 2022-11-26 114142.png)]
**总结:**如果父类是类模板,子类需要指定出父类T的数据类型
6,类模板成员函数类外实现
图示
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-3JpzOPm0-1676032966238)(C:\Users\y’z\Desktop\C++\c++核心编程\屏幕截图 2022-11-26 114142.png)]
**总结:**类模板中成员函数类外实现时,需要加上模板参数列表
7,类模板分文件编写
掌握类模板成员函数分文件编写产生的问题以及解决方式
问题:
类模板中成员函数创建时机是在调用阶段,导致分文件编写时链接不到解决:
解决方式1:直接包含.cpp源文件
解决方式2∶将声明和实现写到同一个文件中,并更改后缀名为.hpp,hpp是约定的名称,并不是强制
图示
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-xoIwSNgt-1676032966238)(C:\Users\y’z\Desktop\C++\c++核心编程\屏幕截图 2022-11-26 191344.png)]
**总结:**主流的解决方式是第工种,将类模板成员函数写到一起,并将后缀名改为.hpp
8,类模板与友元
掌握类模板配合友元函数的类内和类外实现
全局函数类内实现-直接在类内声明友元即可
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-kg49bk9b-1676032966238)(C:\Users\y’z\Desktop\C++\c++核心编程\屏幕截图 2022-11-26 192946.png)]
全局函数类外实现–需要提前让编译器知道全局函数的存在
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-nLeMODqW-1676032966238)(C:\Users\y’z\Desktop\C++\c++核心编程\屏幕截图 2022-11-26 194043.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-zIr0lP9V-1676032966239)(C:\Users\y’z\Desktop\C++\c++核心编程\屏幕截图 2022-11-26 194203.png)]
**总结:**建议全局函数做类内实现,用法简单,而且编译器可以直接识别
9,类模板案例
案例描述:实现一个通用的数组类,要求如下:
(1)可以对内置数据类型以及自定义数据类型的数据进行存储
(2)将数组中的数据存储到堆区
(3)构造函数中可以传入数组的容量
(4)提供对应的拷贝构造函数以及operator=防止浅拷贝问题
(5)提供尾插法和尾删法对数组中的数据进行增加和删除
(6)可以通过下标的方式访问数组中的元素
(7)可以获取数组中当前元素个数和数组的容量
MyArray.hpp内
#pragma once
#include<iostream>
using namespace std;
template<class T>
class MyArray
{
public:
//有参构造
MyArray(int capacity)
{
//cout << "有参构造" << endl;
this->m_Capacity = capacity;
this->m_Size = 0;
this->pAddress = new T[this->m_Capacity];
}
MyArray(const MyArray& arr)
{
//cout << "拷贝构造" << endl;
//浅拷贝
this->m_Capacity = arr.m_Capacity;
this->m_Size = arr.m_Size;
//深拷贝
this->pAddress = new T[arr.m_Capacity];
for (int i = 0; i < this->m_Size; i++)
{
this->pAddress[i] = arr.pAddress[i];
}
}
MyArray& operator=(const MyArray& arr)
{
//cout << " operator=调用" << endl;
if (this->pAddress != NULL)
{
delete[]this->pAddress;
this->pAddress = NULL;
this->m_Capacity = 0;
this->m_Size = 0;
}
this->m_Capacity = arr.m_Capacity;
this->m_Size = arr.m_Size;
//深拷贝
this->pAddress = new T[arr.m_Capacity];
for (int i = 0; i < this->m_Size; i++)
{
this->pAddress[i] = arr.pAddress[i];
}
return *this;
}
//尾插法
void Push_Back(const T& val)
{
//判断容量是否等于大小
if (this->m_Capacity == this->m_Size)
{
return;
}
this->pAddress[this->m_Size] = val;//在数组末尾插入数据
this->m_Size++;//更新数组大小
}
void Pop_Back()
{
if (this->m_Size == 0)
{
return;
}
this->m_Size--;
}
T& operator[](int index)
{
return this->pAddress[index];
}
//获取数组容量
int getCapacity()
{
return this->m_Capacity;
}
//获取数组大小
int getSize()
{
return this->m_Size;
}
~MyArray()
{
//cout << "析构函数" << endl;
if (this->pAddress != NULL)
{
delete[]this->pAddress;
this->pAddress = NULL;
}
}
private:
int m_Capacity;//容量即为最大
int m_Size;//大小即为目前数组内有多少数据
T* pAddress;//指针指向堆区开辟的数组
};
数组类封装.cpp内
#define _CRT_SECURE_NO_WARNINGS 1
#include<iostream>
using namespace std;
#include"MyArray.hpp"
class Person
{
public:
string m_Name;
int m_Age;
};
void printPersonArray(MyArray<Person>arr)
{
for (int i = 0; i < arr.getSize(); i++)
{
cout << "姓名" << arr[i].m_Name << "年龄" << arr[i].m_Age << endl;
}
}
void test01()
{
MyArray<Person>arr(10);
printPersonArray(arr);
}
int main()
{
test01();
system("paues");
return 0;
}
2, STL初识
1,STL的诞生
长久以来,软件界一直希望建立—种可重复利用的东西
C++的面向对象和泛型编程思想,目的就是复用性的提升
大多情况下,数据结构和算法都未能有一套标准,导致被迫从事大量重复工作
为了建立数据结构和算法的一套标准,诞生了STL
2, STL基本概念
STL(Standard Template Library,标准模板库)
STL从广义上分为:容器(container)算法(algorithm)迭代器(iterator)
容器和算法之间通过迭代器进行无缝连接
STL几乎所有的代码都采用了模板类或者模板函数
3,STL六大组件
STL大体分为六大组件,分别是:容器、算法,迭代器、仿函数、适配器(配接器)、空间配置器
1.容器:各种数据结构,如vector、list、deque、set、map等,用来存放数据。
2.算法:各种常用的算法,如sort、 find、copy、for_each等
3.迭代器:扮演了容器与算法之间的胶合剂。
4.仿函数:行为类似函数,可作为算法的某种策略。
5.适配器:—种用来修饰容器或者仿函数或迭代器接口的东西。
6.空间配置器:负责空间的配置与管理。
2.4 STL中容器、算法、迭代器
容器:置物之所也
STL容器就是将运用最广泛的一些数据结构实现出来
常用的数据结构:数组,链表,树,戈,队列,集合,映射表等
这些容器分为序列式容器和关联式容器两种:
序列式容器:强调值的排序,序列式容器中的每个元素均有固定的位置。
关联式容器:二叉树结构,各元素之间没有严格的物理上的顺序关系。
算法:问题之解法也
有限的步骤,解决逻辑或数学上的问题,这一门学科我们叫做算法(Algorithms)
算法分为:质变算法和非质变算法。
质变算法:是指运算过程中会更改区间内的元素的内容。例如拷贝,替换,删除等等
非质变算法:是指运算过程中不会更改区间内的元素内容,例如查找、计数、遍历、寻找极值等等
迭代器:容器和算法之间粘合剂
提供一种方法,使之能够依序寻访某个容器所含的各个元素,而又无需暴露该容器的内部表示方式。
每个容器都有自己专属的迭代器
迭代器使用非常类似于指针,初学阶段我们可以先理解迭代器为指针
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-9IAP9tII-1676032966239)(C:\Users\y’z\Desktop\C++\c++核心编程\屏幕截图 2022-11-27 185217.png)]
5,容器算法迭代器初识
了解STL中容器、算法、迭代器概念之后,我们利用代码感受STL的魅力
STL中最常用的容器为Vector,可以理解为数组,下面我们将学习如何向这个容器中插入数据、并遍历这个容器
1,vector存放内置数据类型
容器:
vector
算法:
for_each
迭代器:
vector<int> : :iterator
图示
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ogU0UiDq-1676032966239)(C:\Users\y’z\Desktop\C++\c++核心编程\屏幕截图 2022-11-27 193530.png)]
例
#define _CRT_SECURE_NO_WARNINGS 1
#include<iostream>
using namespace std;
#include<vector>//容器头文件
#include<algorithm>//标准算法头文件
void myPrint(int val)
{
cout << val << endl;
}
void test01()
{
vector<int>v;
v.push_back(10);
v.push_back(20);
v.push_back(30);
v.push_back(40);
//迭代器
vector<int>::iterator itBegin = v.begin();//起始迭代器
vector<int>::iterator itEnd = v.end();//结束迭代器
//第一种遍历方式
while (itBegin != itEnd)
{
cout << *itBegin << endl;
itBegin++;
}
//第二种遍历方式
for (vector<int>::iterator it = v.begin(); it != v.end(); it++)
{
cout << *it << endl;
}
//第三种遍历方式 利用STL提供的遍历算法
for_each(v.begin(), v.end(), myPrint);
}
int main()
{
test01();
system("pause");
return 0;
}
2, Vector存放自定义数据类型
vector中存放自定义数据类型,并打印输出
例
#define _CRT_SECURE_NO_WARNINGS 1
#include<iostream>
using namespace std;
#include<string>
#include<vector>//容器头文件
#include<algorithm>//标准算法头文件
class Person
{
public:
Person(string name, int age)
{
this->m_Name = name;
this->m_Age = age;
}
string m_Name;
int m_Age;
};
void test01()
{
vector<Person>v;
Person p1("aaa", 10);
Person p2("bbb", 20);
Person p3("ccc", 30);
Person p4("ddd", 40);
Person p5("eee", 50);
//向容器中添加数据
v.push_back(p1);
v.push_back(p2);
v.push_back(p3);
v.push_back(p4);
v.push_back(p5);
//遍历数据
for (vector<Person>::iterator it = v.begin(); it != v.end(); it++)
{
cout << "姓名" << it->m_Name << "年龄" << (*it).m_Age << endl;
}//*解引用解出来为Person的数据类型
}
void test02()
{
vector<Person*>v;//即为把下面五个数据的地址保存
Person p1("aaa", 10);
Person p2("bbb", 20);
Person p3("ccc", 30);
Person p4("ddd", 40);
Person p5("eee", 50);
//向容器中添加数据
v.push_back(&p1);//则要加取址
v.push_back(&p2);
v.push_back(&p3);
v.push_back(&p4);
v.push_back(&p5);
for (vector<Person*>::iterator it = v.begin(); it != v.end(); it++)
{
cout << "姓名" << (*it)->m_Name << "年龄" << (*it)->m_Age << endl;
}
}
int main()
{
test01();
test02();
return 0;
}
3,Vector容器嵌套容器
学习目标:容器中嵌套容器,我们将所有教据讲行遍历输出
例
#define _CRT_SECURE_NO_WARNINGS 1
#include<iostream>
using namespace std;
#include<string>
#include<vector>//容器头文件
#include<algorithm>//标准算法头文件
void test01()
{
vector<vector<int>>v;
//创建小容器
vector<int>v1;
vector<int>v2;
vector<int>v3;
vector<int>v4;
//向小容器添加数组
for (int i = 0; i < 4; i++)
{
v1.push_back(i + 1);
v2.push_back(i + 2);
v3.push_back(i + 3);
v4.push_back(i + 4);
}
//将小容器插入大容器中
v.push_back(v1);
v.push_back(v2);
v.push_back(v3);
v.push_back(v4);
//通过大容器,把所以数据遍历一遍
for (vector<vector<int>>::iterator it = v.begin(); it != v.end(); it++)
{
//(*it)---- 容器vector<int>
for (vector<int>::iterator vit = (*it).begin(); vit != (*it).end(); vit++)
{
cout << *vit << " ";
}
cout << endl;
}
}
int main()
{
test01();
return 0;
}
3,常用容器
3,string容器
1,string容器
string是C++风格的字符串只能存放字符,而string本质上是一个类
string和char*区别:
char*是一个指针
string是一个类,类内部封装了char*,管理这个字符串,是一个char*型的容器
特点:
string类内部封装了很多成员方法
例如:查找find,拷贝copy,删除delete,替换replace,插入insert
string管理char*所分配的内存,不用担心复制越界和取值越界等,由类内部进行负责
2,,string构造函数
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-KNvlSCce-1676032966239)(C:\Users\y’z\Desktop\C++\c++核心编程\屏幕截图 2022-11-27 214146.png)]
const char*str即为创建一个字符串
图示
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-WKCn5hLl-1676032966240)(C:\Users\y’z\Desktop\C++\c++核心编程\屏幕截图 2022-11-27 214617.png)]
3,string赋值操作
给string字符进行赋值
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-cHaWSdrb-1676032966240)(C:\Users\y’z\Desktop\C++\c++核心编程\屏幕截图 2022-11-27 215010.png)]
operator即为重载符号即重载后的=号
代码图示
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Ie6q0MID-1676032966240)(C:\Users\y’z\Desktop\C++\c++核心编程\屏幕截图 2022-11-27 215840.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-j1PwT1YQ-1676032966240)(C:\Users\y’z\Desktop\C++\c++核心编程\屏幕截图 2022-11-27 215942.png)]
总结:
string的赋值方式很多**operator=**比较实用。
3.1.4 string字符串拼接
实现在字符串末尾拼接字符串
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-hM1K5GJv-1676032966240)(C:\Users\y’z\Desktop\C++\c++核心编程\屏幕截图 2022-11-28 194412.png)]
代码示例
#define _CRT_SECURE_NO_WARNINGS 1
#include<iostream>
using namespace std;
#include<string>
#include<vector>//容器头文件
#include<algorithm>//标准算法头文件
void test01()
{
string str1 = "我";
str1 += "爱玩游戏";
cout << str1 << endl;
str1 += ',';
cout << str1 << endl;
string str2 = "LOL DNF";
str1 += str2;
cout << str1 << endl;
string str3 = "I ";
str3.append("love ");
cout << str3 << endl;
str3.append("You,absd", 3);
cout << str3 << endl;
str3.append(str2);
cout << str3 << endl;
str3.append(str2, 4, 3);//参数二是从那个位置开始截取
//参数三是截取多少个字符
cout << str3 << endl;
}
int main()
{
test01();
return 0;
}
5,string查找和替换
查找:查找指定字符串是否存在
替换:在指定的位置替换字符串
函数原型:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-bPoHLrdJ-1676032966241)(C:\Users\y’z\Desktop\C++\c++核心编程\屏幕截图 2022-11-28 201632.png)]
查找
查找find函数会返回一个整形如果查到则返回下标即从0开始的下标如果没有查到则返回-1。
rfind和find区别
rfind从右往左查找,find从左往右查找
替换
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-sxkVbhmS-1676032966241)(C:\Users\y’z\Desktop\C++\c++核心编程\屏幕截图 2022-11-28 202747.png)]
总结:
find查找是从左往后,rfind从右往左
find找到字符串后返回查找的第一个字符位置,找不到返回-1
replace在替换时,要指定从哪个位置起,多少个字符,替换成什么样的字符串
6,string字符串比较
功能描述:
字符串之间的比较
比较方式:
字符串比较是按字符的ASCII码进行对比(逐个对比)
= 返回0
大于符号 返回1
< 返回-1
函数原型:
int compare(const string &s) const;
与字符串s比较
int compare(const char *s) const;
与字符串s比较
图示
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-MJI7wNmf-1676032966241)(C:\Users\y’z\Desktop\C++\c++核心编程\屏幕截图 2022-11-30 112707.png)]
**总结:**字符串对比主要是用于比较两个字符串是否相等,判断谁大谁小的意义并不是很大
7,string字符存取
string中单个字符存取方式有两种
char& operator[](int n);
通过[]方式取字符
char& at(int n);
通过at方法获取字符
读字符串图示
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-30CI3NKo-1676032966241)(C:\Users\y’z\Desktop\C++\c++核心编程\屏幕截图 2022-11-28 204439.png)]
修改字符串图示
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-iRRpUJYK-1676032966242)(C:\Users\y’z\Desktop\C++\c++核心编程\屏幕截图 2022-11-28 204645.png)]
string中有size()函数可以返回字符串长度
使用方法
变量名.stze()
8,string插入和删除
功能描述:
对string字符串进行插入和删除字符操作
函数原型:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-qx8GFWg0-1676032966242)(C:\Users\y’z\Desktop\C++\c++核心编程\屏幕截图 2022-11-28 204915.png)]
图示
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-jt9DBHdx-1676032966242)(C:\Users\y’z\Desktop\C++\c++核心编程\屏幕截图 2022-11-28 211505.png)]
**总结:**插入和删除的起始下标都是从0开始
9,string子串
功能描述:
从字符串中获取想要的子串**(子串即为字符串内的一个小部分)**
函数原型:
string substr (int pos = 0, int n = npos) const;
要通过find函数找到需求字符
substr即为子串函数名
返回由pos开始的n个字符组成的字符串
代码示例
#define _CRT_SECURE_NO_WARNINGS 1
#include<iostream>
using namespace std;
#include<string>
#include<vector>//容器头文件
#include<algorithm>//标准算法头文件
void test02()
{
string email = "zhangsan@sina.com";
int pos = email.find("@");//返回需求字符的位置下标
cout << pos << endl;
string usrName = email.substr(0, pos);//查找用户名
cout << usrName << endl;
string usrName = email.substr(pos,email.size());//查找@后缀名
cout << usrName << endl;
}
int main()
{
test02();
return 0;
}
示例
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-qIi2nrca-1676032966242)(C:\Users\y’z\Desktop\C++\c++核心编程\屏幕截图 2022-11-28 212547.png)]
4,vector容器
1,vector基本概念功能:
vector数据结构和数组非常相似,也称为单端数组
vector与普通数组区别:
不同之处在于数组是静态空间,而vector可以动态扩展并且可以有不同的数据类型
动态扩展:
并不是在原空间之后续接新空间,而是找更大的内存空间,然后将原数据拷贝新空间,释放原空间
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-sF4PllCu-1676032966242)(C:\Users\y’z\Desktop\C++\c++核心编程\屏幕截图 2022-11-28 213312.png)]
2,vector构造函数
功能描述:
创建vector容器
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-yolF6Gfx-1676032966243)(C:\Users\y’z\Desktop\C++\c++核心编程\屏幕截图 2022-11-28 213533.png)]
代码示例
#define _CRT_SECURE_NO_WARNINGS 1
#include<iostream>
using namespace std;
#include<string>
#include<vector>//容器头文件
#include<algorithm>//标准算法头文件
void printVector(vector<int>&v)
{
for (vector<int>::iterator it = v.begin(); it != v.end(); it++)
{
cout << *it << " ";
}
cout << endl;
}
void test01()
{
vector<int>v1;//默认构造
for (int i = 0; i < 10; i++)
{
v1.push_back(i);
}
printVector(v1);
//通过区间方式进行构造
vector<int>v2(v1.begin(), v1.end());
printVector(v2);
//通过n个elem方式构造
vector<int>v3(10, 100);//第一个是个数,第二个是数值
printVector(v3);
//拷贝构造
vector<int>v4(v3);
printVector(v4);
}
int main()
{
test01();
return 0;
}
3,vector赋值操作
功能描述:
给vector容器进行赋值
函数原型:
vector& operator=(const vector &vec);
重载等号操作符
assign( beg, end);
将[beg, end)区间中的数据拷贝赋值给本身。
assign(n,elem);
将n个elem拷贝赋值给本身
代码示例
#define _CRT_SECURE_NO_WARNINGS 1
#include<iostream>
using namespace std;
#include<string>
#include<vector>//容器头文件
#include<algorithm>//标准算法头文件
void printVector(vector<int>&v)
{
for (vector<int>::iterator it = v.begin(); it != v.end(); it++)
{
cout << *it << " ";
}
cout << endl;
}
void test01()
{
vector<int>v1;//默认构造
for (int i = 0; i < 10; i++)
{
v1.push_back(i);
}
printVector(v1);
//赋值 operator=
vector<int>v2;
v2 = v1;
printVector(v2);
vector<int>v3;
v3.assign(v1.begin(), v1.end());
printVector(v3);
vector<int>v4;
v4.assign(10,100);
printVector(v4);
}
4,vector容量和大小
功能描述:
对vector容器的容量和大小操作
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-x0UCyeIN-1676032966243)(C:\Users\y’z\Desktop\C++\c++核心编程\屏幕截图 2022-11-28 220409.png)]
代码示例
#define _CRT_SECURE_NO_WARNINGS 1
#include<iostream>
using namespace std;
#include<string>
#include<vector>//容器头文件
#include<algorithm>//标准算法头文件
void printVector(vector<int>&v)
{
for (vector<int>::iterator it = v.begin(); it != v.end(); it++)
{
cout << *it << " ";
}
cout << endl;
}
void test01()
{
vector<int>v1;//默认构造
for (int i = 0; i < 10; i++)
{
v1.push_back(i);
}
printVector(v1);
if (v1.empty())//为真 代表容器为空
{
cout << "v1为空" << endl;
}
else
{
cout << "v1不为空" << endl;
cout << "v1容量为" << v1.capacity() << endl;
//可以通过capacity来查看容器容量
cout << "v1的大小" << v1.size() << endl;
}
//重新指定大小
v1.resize(15,100);//利用重载版本,可以指定默认填充值,参数2
printVector(v1); //如果重新指定的比原来长了,默认用0填充新的位置
v1.resize(5);
printVector(v1);//如果重新指定的比原来短了,超出部分会删除掉
}
总结:
判断是否为空— empty
返回元素个数— size
返回容器容量— capacity
重新指定大小— resize
5,vector插入和删除
功能描述:
对vector容器进行插入、删除操作
函数原型:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-PQtniW5X-1676032966243)(C:\Users\y’z\Desktop\C++\c++核心编程\屏幕截图 2022-11-29 130443.png)]
代码示例
#define _CRT_SECURE_NO_WARNINGS 1
#include<iostream>
using namespace std;
#include<string>
#include<vector>//容器头文件
#include<algorithm>//标准算法头文件
void printVector(vector<int>&v)
{
for (vector<int>::iterator it = v.begin(); it != v.end(); it++)
{
cout << *it << " ";
}
cout << endl;
}
void test01()
{
vector<int>v1;//默认构造
for (int i = 0; i < 10; i++)
{
v1.push_back(i);
}
printVector(v1);
//尾删
v1.pop_back();
printVector(v1);
//插入 第一个参数为迭代器
v1.insert(v1.begin(), 100);
printVector(v1);
v1.insert(v1.begin(), 2, 1000);
printVector(v1);
//删除 参数也是迭代器
v1.erase(v1.begin());
printVector(v1);
//清空
v1.erase(v1.begin(), v1.end());//类似于v1.clear()都为清空操作
printVector(v1);
v1.clear();
printVector(v1);
}
6,vector数据存取
功能描述;
对vector中的数据的存取操作
函数原型:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-nVeKRwkW-1676032966243)(C:\Users\y’z\Desktop\C++\c++核心编程\屏幕截图 2022-11-29 191525.png)]
代码示例
#define _CRT_SECURE_NO_WARNINGS 1
#include<iostream>
using namespace std;
#include<string>
#include<vector>//容器头文件
#include<algorithm>//标准算法头文件
void printVector(vector<int>&v)
{
for (vector<int>::iterator it = v.begin(); it != v.end(); it++)
{
cout << *it << " ";
}
cout << endl;
}
void test01()
{
vector<int>v1;//默认构造
for (int i = 0; i < 10; i++)
{
v1.push_back(i);
}
printVector(v1);
//利用[]访问数组中元素
for (int i = 0; i < v1.size(); i++)
{
cout << v1[i] << " ";
}
cout << endl;
//利用at方式访问元素
for (int i = 0; i < v1.size(); i++)
{
cout << v1.at(i) <<" ";
}
cout << endl;
//获取第一个元素
cout << "第一个元素为" << v1.front() << endl;
//获取最后一个元素
cout << "最后一个元素为" << v1.back() << endl;
}
总结:
除了用迭代器获取vector容器中元素,[]和at也可以.
front返回容器第一个元素
back返回容器最后一个元素
7,vector互换容器
功能描述:
实现两个容器内元素进行互换
函数原型:
Swap(vec);
将vec与本身的元素互换
代码示例
ine _CRT_SECURE_NO_WARNINGS 1
#include<iostream>
using namespace std;
#include<string>
#include<vector>//容器头文件
#include<algorithm>//标准算法头文件
void printVector(vector<int>&v)
{
for (vector<int>::iterator it = v.begin(); it != v.end(); it++)
{
cout << *it << " ";
}
cout << endl;
}
void test01()
{
vector<int>v1;//默认构造
for (int i = 0; i < 10; i++)
{
v1.push_back(i);
}
cout << "交换前"<<endl;
printVector(v1);
vector<int>v2;
for (int i = 10; i != 0; i--)
{
v2.push_back(i);
}
printVector(v2);
cout << "交换后" << endl;
v1.swap(v2);
printVector(v1);
printVector(v2);
}
//实际用途
// swap可以收缩内存空间
void test02()
{
vector<int>v;
for(int i=0;i<10000;i++)
{
v.push_back(i);
}
cout << "v的容量" << v.capacity() << endl;
cout << "v的大小" << v.size() << endl;
v.resize(3);
cout << "v的容量" << v.capacity() << endl;
cout << "v的大小" << v.size() << endl;
//swap可以收缩内存空间
vector<int>(v).swap(v);
cout << "v的容量" << v.capacity() << endl;
cout << "v的大小" << v.size() << endl;
}
注意事项
vector<int>(v).swap(v);
vector(v)即初始化一个匿名对象(在使用后自动消除)
.swap(v);容器交换
8,vector预留空间
功能描述:
减少vector在动态扩展容量时的扩展次数
函数原型:(reserve保留)
区分reserve(保留)和reverse(逆向)出现在list容器反转
reserve(int len);
容器预留len个元素长度,预留位置不初始化,元素不可访问。
代码示例
#define _CRT_SECURE_NO_WARNINGS 1
#include<iostream>
using namespace std;
#include<string>
#include<vector>//容器头文件
#include<algorithm>//标准算法头文件
void printVector(vector<int>&v)
{
for (vector<int>::iterator it = v.begin(); it != v.end(); it++)
{
cout << *it << " ";
}
cout << endl;
}
void test01()
{
vector<int>v1;//默认构造
//利用reserve预留空间
v1.reserve(100000);
int num = 0;
int* p = NULL;
for (int i = 0; i < 100000; i++)
{
v1.push_back(i);
if (p != &v1[0])//即p等于首地址则说明数组未开辟新空间
//如果p不等于首地址则说明开辟了新空间,原首地址失效
//则令p改为新的数组地址并num加一
{
p = &v1[0];
num++;
}
}
cout << num << endl;
}
**总结:**如果教据量较大,可以一开始利用reserve预留空间
5,deque容器
1,deque容器基本概念
功能:
双端数组,可以对头端进行插入删除操作
deque与vector区别:
vector对于头部的插入删除效率低,数据量越大,效率越低
deque相对而言,对头部的插入删除速度回比vector快
vector访问元素时的速度会比deque快,这和两者内部实现有关
图示
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-lahSkjWc-1676032966244)(C:\Users\y’z\Desktop\C++\c++核心编程\屏幕截图 2022-11-29 211030.png)]
deque内部工作原理:
deque内部有个中控器,维护每段缓冲区中的内容,缓冲区中存放真实数据
中控器维护的是每个缓冲区的地址,使得使用deque时像一片连续的内存空间
图示
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-f2dE2na9-1676032966244)(C:\Users\y’z\Desktop\C++\c++核心编程\屏幕截图 2022-11-29 211707.png)]
2,deque构造函数
功能描述:
deque容器构造
函数原型:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-wGWJ317s-1676032966244)(C:\Users\y’z\Desktop\C++\c++核心编程\屏幕截图 2022-11-29 211853.png)]
代码示例
#define _CRT_SECURE_NO_WARNINGS 1
#include<iostream>
#include<deque>
using namespace std;
void printDeque(const deque<int>& d)//设置函数只读
{
for (deque<int>::const_iterator it = d.begin(); it < d.end(); it++)
{
cout << *it << " ";
}
cout<< endl;
}
void test01()
{
deque<int>d1;
for (int i = 0; i < 10; i++)
{
d1.push_back(i);//迭代器访问
}
printDeque(d1);
deque<int>d2(d1.begin(), d1.end());
printDeque(d2);
deque<int>d3(10,100);
printDeque(d3);
deque<int>d4(d3);
printDeque(d4);
}
int main()
{
test01();
return 0;
}
注意事项(只读迭代器)
const_iterator
总结: deque容器和vector容器的构造方式几乎一致,灵活使用即可
3,deque赋值操作
功能描述;
·给deque容器进行赋值
函数原型:
deque&~operator=(const deque &deq);
重载等号操作符
assign(beg,end);
将[beg, end)区间中的数据拷贝赋值给本身。
assign(n,elem);
将n个elem拷贝赋值给本身。
图示
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-xvKBK2A8-1676032966244)(C:\Users\y’z\Desktop\C++\c++核心编程\屏幕截图 2022-11-29 213946.png)]
4,deque大小操作
功能描述:
对deque容器的大小进行操作
函数原型图示
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-XuuMF5D6-1676032966244)(C:\Users\y’z\Desktop\C++\c++核心编程\屏幕截图 2022-11-29 214154.png)]
注意事项
deque容器没有容器的概念即无(capacity()函数)可以无限的放数据
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-BQTz8Rld-1676032966245)(C:\Users\y’z\Desktop\C++\c++核心编程\屏幕截图 2022-11-29 214859.png)]
总结:
deque没有容量的概念
判断是否为空—empty
返回元素个数— size
重新指定个数— resize
5,deque插入和删除
功能描述:
向deque容器中插入和删除数据
函数原型:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-T2xqGzZ3-1676032966245)(C:\Users\y’z\Desktop\C++\c++核心编程\屏幕截图 2022-11-29 215157.png)]
两端插入操作图示
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-rAnvmQpw-1676032966245)(C:\Users\y’z\Desktop\C++\c++核心编程\屏幕截图 2022-11-29 220122.png)]
指定位置操作插入图示
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-skKcLzMS-1676032966245)(C:\Users\y’z\Desktop\C++\c++核心编程\屏幕截图 2022-11-29 220311.png)]
指定位置操作删除图示
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Lx2vNCoE-1676032966245)(C:\Users\y’z\Desktop\C++\c++核心编程\屏幕截图 2022-11-29 220338.png)]
总结:
插入和删除提供的位置是迭代器
尾插— push_back
尾删— pop_back
头插---- push_front
头删— pop_front
6,deque数据存取
功能描述:
对deque中的数据的存取操作
函数原型:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-5XjtwLU7-1676032966246)(C:\Users\y’z\Desktop\C++\c++核心编程\屏幕截图 2022-11-29 220657.png)]
代码图示
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-EIGV9FJS-1676032966246)(C:\Users\y’z\Desktop\C++\c++核心编程\屏幕截图 2022-11-29 221030.png)]
总结:
除了用迭代器获取deque容器中元素,[]和at也可以.
front返回容器第一个元素
back返回容器最后一个元素
7,deque排序
功能描述:
利用算法实现对deque容器进行排序
算法:
要使用标准算法头文件#include
sort(iterator beg,iterator end)
对beg和end区间内元素进行排序**(默认排序规则 从小到大 升序)**
对于支持随机访问的迭代器的容器,都可以利用sort算法直接对其进行排序
vector容器也可以利用sort进行排序
STL案例,评委打分
1,案例描述
有5名选手:选手ABCDE,10个评委分别对每一名选手打分,去除最高分,去除评委中最低分,取平均分。
2,实现步骤
1,创建五名选手,放到vector中
2,遍历vector容器,取出来每一个选手,执行for循环,可以把10个评分打分存到deque容器中
3,sort算法对deque容器中分数排序,去除最高和最低分
4,deque容器遍历一遍,累加总分
5,获取平均分
代码示例
#define _CRT_SECURE_NO_WARNINGS 1
#include<iostream>
#include<vector>
#include<string>
#include<deque>
#include<algorithm>
#include<ctime>
using namespace std;
class Person//创建人类
{
public:
Person(string name, int score)
{
this->m_Name = name;
this->m_Score = score;
}
string m_Name;
int m_Score;
};
void createPerson(vector<Person>&p)//创建人的属性
{
string nameSeed = "ABCDE";
for (int i = 0; i < 5; i++)
{
string name = "选手";//通过string容器的重载+=来进行字符拼接
name += nameSeed[i];
int score = 0;//初始化每个分数
Person per(name, score);
p.push_back(per);//将每个成员放入vector
}
}
void setScore(vector<Person>&p)
{
cout << "选手得分如下" << endl;
for (vector<Person>::iterator it = p.begin(); it < p.end(); it++)
{
deque<int>d;
for (int i = 0; i < 10; i++)
{
int score= rand() % 41 + 60;//使用随机数
d.push_back(score);
}
cout << "姓名 " << (*it).m_Name << " 得分 ";
for (deque<int>::iterator dit = d.begin(); dit < d.end(); dit++)
{
cout << *dit << " ";
}
cout << endl;
//排序
sort(d.begin(), d.end());
//去除最高和最低分
d.pop_back();
d.pop_front();
//取平均分
int sum=0;
for (deque<int>::iterator dit = d.begin(); dit < d.end(); dit++)
{
sum += *dit;
}
int avg = 0;
avg = sum / d.size();
//将平均放赋值给选手
it->m_Score = avg;
}
}
void showPerson(vector<Person>&p)//展示数组
{
cout << "去掉一个最高分去掉一个最低分后" << endl;
for (vector<Person>::iterator it = p.begin(); it < p.end(); it++)
{
cout << "最终 "<< (*it).m_Name <<" 平均分为 " << (*it).m_Score<< endl;
}
}
void test01()
{
vector<Person>p;
createPerson(p);
setScore(p);
showPerson(p);
}
int main()
{
srand((unsigned int)time(NULL));//随机数种子
test01();
return 0;
}
6,stack容器
1,stack基本概念
概念: stack是一种先进后出(栈容器)(First In Last Out,FILO)的数据结构,它只有一个出口
图示
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-CIwk2B0l-1676032966246)(C:\Users\y’z\Desktop\C++\c++核心编程\屏幕截图 2022-11-30 204848.png)]
栈中只有顶端的元素才可以被外界使用,因此栈不允许有遍历行为
栈中进入数据称为—入栈push
栈中弹出数据称为—出栈pop
2,stack常用接口
**功能描述:**栈容器常用的对外接口
构造函数:
stack<T> stk;
stack采用模板类实现, stack对象的默认构造形式
stack(const stack &stk);
拷贝构造函数
赋值操作:
stack& operator=( const stack &stk);
重载等号操作符
数据存取:
push(elem);
向栈顶添加元素
pop();
从栈顶移除第一个元素
top();
返回栈顶元素
大小操作:
empty();
判断堆栈是否为空
size();
返回栈的大小
代码图示
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-QUFC1nr3-1676032966246)(C:\Users\y’z\Desktop\C++\c++核心编程\屏幕截图 2022-11-30 213640.png)]
总结:
入栈 — pushI
出栈— pop
返回栈顶— top
入栈 — pushI
出栈— pop
返回栈顶— top
7,queue容器
1,queue基本概念
**概念:**queue是一种(队列容器)先进先出(First ln First Out,FIFO)的数据结构,它有两个出口
图示
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-jCMzbueW-1676032966247)(C:\Users\y’z\Desktop\C++\c++核心编程\屏幕截图 2022-11-30 214029.png)]
队列容器允许从一端新增元素,从另一端移除元素
队列中只有队头和队尾才可以被外界使用,因此队列不允许有遍历行为
队列中进数据称为—入队 push
队列中出数据称为—出队 pop
2,queue常见接口
图示
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-P7dWtzBM-1676032966247)(C:\Users\y’z\Desktop\C++\c++核心编程\屏幕截图 2022-11-30 214646.png)]
总结:
入队 — push
出队— pop
返回队头元素— front
返回队尾元素— back
判断队是否为空— empty
返回队列大小— size
8,list容器
1,list基本概念
**功能:**将数据进行链式存储
链表(list)是一种物理存储单元上非连续的存储结构,数据元素的逻辑顺序是通过链表中的指针链接实现的
链表的组成:链表由一系列结点组成
结点的组成:一个是存储数据元素的数据域,另一个是存储下一个结点地址的指针域
STL中的链表是—个双向循环链表
图示
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-rK57aR62-1676032966247)(C:\Users\y’z\Desktop\C++\c++核心编程\屏幕截图 2022-11-30 221107.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-fDV7Y9JG-1676032966247)(C:\Users\y’z\Desktop\C++\c++核心编程\屏幕截图 2022-11-30 220348.png)]
由于链表的存储方式并不是连续的内存空间,因此链表list中的迭代器只支持前移和后移,属于双向迭代器
list的优点:
采用动态存储分配,不会造成内存浪费和溢出
链表执行插入和删除操作十分方便,修改指针即可,不需要移动大量元素
list的缺点:
链表灵活,但是空间(指针域)和时间(遍历)额外耗费较大
List有一个重要的性质,插入操作和删除操作都不会造成原有list迭代器的失效**(指地址是否失效)**,这在vector是不成立的。
总结:STL中List和vector是两个最常被使用的容器,各有优缺点
2,list构造函数
功能描述:
创建list容器
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-OktuUvo1-1676032966248)(C:\Users\y’z\Desktop\C++\c++核心编程\屏幕截图 2022-12-01 191730.png)]
代码图示
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-WxjW6O7Z-1676032966248)(C:\Users\y’z\Desktop\C++\c++核心编程\屏幕截图 2022-12-01 192300.png)]
**总结:**list构造方式同其他几个STL常用容器,熟练掌握即可
3,list赋值和交换
功能描述:
给list容器进行赋值,以及交换list容器
函数原型:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ngk2Uk43-1676032966248)(C:\Users\y’z\AppData\Roaming\Typora\typora-user-images\image-20221201192613781.png)]
赋值代码示例
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-VVHxuq1F-1676032966248)(C:\Users\y’z\Desktop\C++\c++核心编程\屏幕截图 2022-12-01 193055.png)]
交换代码示例
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-SGZpDe0U-1676032966248)(C:\Users\y’z\Desktop\C++\c++核心编程\屏幕截图 2022-12-01 193149.png)]
4,list大小操作
功能描述:
对list容器的大小进行操作
函数原型:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Fy2uawd8-1676032966249)(C:\Users\y’z\Desktop\C++\c++核心编程\屏幕截图 2022-12-01 193450.png)]
代码示例
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-9v0feoVq-1676032966249)(C:\Users\y’z\Desktop\C++\c++核心编程\屏幕截图 2022-12-01 194109.png)]
总结:
判断是否为空–Fmpty
返回元素个数— size
重新指定个数— resize
5,list插入和删除
功能描述:
对list容器进行数据的插入和删除
函数原型:
push_back(elem);在容器尾部加入一个元素
pop_back(); 删除容器中最后一个元素
push_front(elem);在容器开头插入一个元素
pop_front(); 从容器开头移除第一个元素
insert(pos,elem);在pos位置插elem元素的拷贝,返回新数据的位置。
insert(pos,n,elem);在pos位置插入n个elem数据,无返回值。
insert(pos,elem);在pos位置插elem元素的拷贝,返回新数据的位置。
insert(pos,n,elem);在pos位置插入n个elem数据,无返回值。
insert(pos,beg,end);在pos位置插入[beg,end)区间的数据,无返回值。
clear();移除容器的所有数据
erase(beg,end);删除[beg,end)区间的数据,返回下一个数据的位置。
erase(pos);别除pos位置的数据,返回下一个数据的位置。
remove(elem);删除容器中所有与elem值匹配的元素。
代码图示
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-yCK7TsBw-1676032966249)(C:\Users\y’z\Desktop\C++\c++核心编程\屏幕截图 2022-12-01 204723.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-3SQoFDVZ-1676032966249)(C:\Users\y’z\Desktop\C++\c++核心编程\屏幕截图 2022-12-01 204838.png)]
总结:
尾插— puph_back
尾制— pop_back
头插— push_front
头删— pop_front
插入— insert(括号内要提供迭代器可以创建一个迭代器在进行迭代器)
图示
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-bleEKDwj-1676032966249)(C:\Users\y’z\Desktop\C++\c++核心编程\屏幕截图 2022-12-01 205414.png)]
删除— erase()
移除— remove
清空— clear
6,list数据存取
功能描述:
对list容器中数据进行存取
由于list底层是链表,不是用连续线性空间存取数据,故无法通过at()或[]下标来访问数组,即随机访问
图示
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Ay1O9YoA-1676032966250)(C:\Users\y’z\Desktop\C++\c++核心编程\屏幕截图 2022-12-01 210444.png)]
函数原型:
front();
返回第一个元素。
back();
返回最后一个元素。
代码图示
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-uOtNaaLs-1676032966250)(C:\Users\y’z\Desktop\C++\c++核心编程\屏幕截图 2022-12-01 210644.png)]
总结:
list容器中不可以通过[]或者at方式访问数据
返回第一个元素— front
返回最后一个元素— back
7,list反转和排序
功能描述
将容器中的元素反转,以及将容器中的数据进行排序
区分reserve(保留)出现在vector容器预留空间和reverse(逆向)
函数原型:
reverse();
反转链表
sort();
所有不支持随机访问迭代器的容器,不可以用标准算法
不支持随机访问迭代器的容器,内部会提供对应一些算法
即不支持
sort(L1.begin(),L1.end()) ;
要使用
对象名.sort()//默认从小到大 升序
要使用降序可以先sort()排序在用revese()反转
或提供一个函数或仿函数改变sort()
示例
bool myCompare(int v1,intv2)
{
//降序 就让第一个数>第二个数
return v1>v2;
}
对象名.sort(myCompare);
链表排序
代码图示
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-O5WhSv7T-1676032966250)(C:\Users\y’z\Desktop\C++\c++核心编程\屏幕截图 2022-12-01 212451.png)]
8,排序案例
**案例描述:**将Person自定义数据类型进行排序,Person中属性有姓名、年龄、身高
**排序规则:**按照年龄进行升序,如果年龄相同按照身高进行降序
代码示例
#define _CRT_SECURE_NO_WARNINGS 1
#include<iostream>
using namespace std;
#include<string>
#include<list>//容器头文件
#include<algorithm>//标准算法头文件
class Person
{
public:
Person(string name, int age, int height)
{
this->m_Name = name;
this->m_Age = age;
this->m_Height = height;
}
public:
string m_Name;
int m_Age;
int m_Height;
};
bool operator!=(Person& p, Person& q)//全局函数重载!=号
{
if (p.m_Age != q.m_Age)//如果m_Name和m_Age有一个不一样则返回true
{
return true;
};
return false;
}
//排序规则
bool paixu(Person&p1,Person&p2)
{
if (p1 != p2)//可以直接p1.m_Age!=p2.m_Age即可不用重载!=号
{
return p1.m_Age < p2.m_Age;//升序
}
return p1.m_Height > p2.m_Height;//降序
}
//bool paixu(Person& p1, Person& p2)第二种方法
//{
//if (p1.m_Age == p2.m_Age)
//{
//return p1.m_Height > p2.m_Height;
//}
//return p1.m_Age < p2.m_Age;
//}
void test01()
{
Person p1("张三", 18, 160);
Person p2("李四", 17, 160);
Person p3("王五", 17, 190);
Person p4("赵磊", 19, 170);
list<Person>L1;
L1.push_back(p1);
L1.push_back(p2);
L1.push_back(p3);
L1.push_back(p4);
for (list<Person>::const_iterator it = L1.begin(); it != L1.end(); it++)
{
cout << "姓名" << (*it).m_Name << "年龄" << (*it).m_Age <<"身高" <<(*it).m_Height << endl;
}
cout << "排序后" << endl;
L1.sort(paixu);//对应自定义数据类型排序必须指定规则
for (list<Person>::const_iterator it = L1.begin(); it != L1.end(); it++)
{
cout << "姓名" << (*it).m_Name << "年龄" << (*it).m_Age << "身高" << (*it).m_Height << endl;
}
}
int main()
{
test01();
return 0;
}
总结:
对于自定义数据类型,必须要指定排序规则,否则编译器不知道如何进行排序
高级排序只是在排序规则上再进行—次逻辑规则制定,并不复杂
9,set/multiset容器
1,set(集合容器)基本概念
简介:
所有元素都会在插入时自动被排序
本质:
set/multiset属于关联式容器,底层结构是用二叉树实现。
set和multiset区别:
set不允许容器中有重复的元素.
multiset允许容器中有重复的元素
2,set构造和赋值
**功能描述:**创建set容器以及赋值
构造:
set<T> st;//默认构造函数:
set(const set &st;//拷贝构造函数
赋值:
set& operator=( const set &st//重载等号操作符
代码图示
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-FiKPb9pZ-1676032966250)(C:\Users\y’z\Desktop\C++\c++核心编程\屏幕截图 2022-12-02 160159.png)]
总结
1,在插入数据时**只有insert()**可以使用,并无push_back()
2,set容器特点:所有元素插入时候自动被排序**(升序)**
3,set容器不允许插入重复值
3,set大小和交换
功能描述:
统计set容器大小以及交换set容器
函数原型:
size();//返回容器中元素的数目
empty();//判断容器是否为空
swsap(st);//交换两个集合容器
大小代码图示
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-szSyHKVb-1676032966251)(C:\Users\y’z\Desktop\C++\c++核心编程\屏幕截图 2022-12-02 161211.png)]
交换代码图示
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ki3LwrwA-1676032966251)(C:\Users\y’z\Desktop\C++\c++核心编程\屏幕截图 2022-12-02 161533.png)]
总结:
统计大小— size
判断是否为空— empty
交换容器— swap
4,set插入和删除
功能描述:
set容器进行插入数据和删除数据
函数原型:
insert(elem) //在容器中插入元素。
clear();//清除所有元素
erase(pos);//删除pos迭代器所指的元素,返回下一个元素的迭代器。
erase(beg, end);//册制除区间[beg.end)的所有元素,返回下一个元素的迭代器。
erase(elem);//制除容器中值为elem的元素。
代码示例
#define _CRT_SECURE_NO_WARNINGS 1
#include<iostream>
using namespace std;
#include<set>;
void PrintSet(set<int>s)
{
for (set<int>::iterator it = s.begin(); it != s.end(); it++)
{
cout << *it << " ";
}
cout << endl;
}
void test01()
{
set<int>s1;
s1.insert(40);
s1.insert(30);
s1.insert(10);
s1.insert(20);
//遍历
PrintSet(s1);
//删除
s1.erase(s1.begin());//因为将数据插入容器中已经自动排序
//故删除一号数据为10
PrintSet(s1);
//删除重载版本
s1.erase(30);
PrintSet(s1);
//清空
s1.erase(s1.begin(), s1.end());
s1.clear();
PrintSet(s1);
}
int main()
{
test01();
return 0;
}
总结:
插入— irsert
删除—erase
清空—clear
5,set查找和统计
功能描述:
对set容器进行查找数据以及统计数据
函数原型:
find(key );//查找key是否存在,若存在,返回该键的元素的迭代器;若不存在,返回set.end();
count( key );//统计key的元素个数
代码图示
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-rgzZnl2h-1676032966251)(C:\Users\y’z\Desktop\C++\c++核心编程\屏幕截图 2022-12-02 165200.png)]
总结:
查找— find (返回的是迭代器)
统计— count(对于set,结果为0或者1,因为没有重复数据,对于multset()则有多种可能)
6,set和multiset区别
学习目标:
掌握set和multiset的区别
区别:
1,set不可以插入重复数据,而multiset可以
2,set插入数据的同时会返回插入结果,表示插入是否成功.
3,multiset不会检测数据,因此可以插入重复数据
代码图示
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-MdC6ffCr-1676032966251)(C:\Users\y’z\Desktop\C++\c++核心编程\屏幕截图 2022-12-02 170957.png)]
总结:
set会返回一个pair对组内有迭代器和一个bool类型
如果不允许插入重复数据可以利用set
如果需要插入重复数据利用multiset
7,pair对组创建
功能描述:
成对出现的教据,利用对组可以返回两个数据
两种创建方式:
pair<type,type> p ( value1, value2 );
pair<type,type> p = make_pair( value1, value2 );
代码示例
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-hZ0P8cjQ-1676032966251)(C:\Users\y’z\Desktop\C++\c++核心编程\屏幕截图 2022-12-02 195245.png)]
8,set容器排序
学习目标:
set容器默认排序规则为从小到大,掌握如何改变排序规则
主要技术点:
利用仿函数。可以改变排序规则
代码示例
#define _CRT_SECURE_NO_WARNINGS 1
#include<iostream>
using namespace std;
#include<set>
#include<algorithm>
void PrintSet(set<int>s)
{
for (set<int>::iterator it = s.begin(); it != s.end(); it++)
{
cout << *it << " ";
}
cout << endl;
}
class Mycompare//创建一个类,内置仿函数
{
public:
bool operator()(int v1, int v2) const
{//注意要加const在后面
return v1 > v2;//降序
}
};
void test01()
{
set<int>s1;
s1.insert(10);
s1.insert(20);
s1.insert(30);
s1.insert(40);
s1.insert(50);
PrintSet(s1);
set<int,Mycompare>s2;
//set容器在插入数据后就不可以改变即排好序
//所以必须在插之前指定规则
s2.insert(10);
s2.insert(20);
s2.insert(30);
s2.insert(40);
s2.insert(50);
for (set<int, Mycompare>::iterator it = s2.begin(); it != s2.end(); it++)//循环时用不同的迭代器
{
cout << *it << " ";
}
cout << endl;
}
int main()
{
test01();
return 0;
}
对于自定义数据类型,set必须指定排序规则才可以插入数据
代码示例
#define _CRT_SECURE_NO_WARNINGS 1
#include<iostream>
using namespace std;
#include<set>
#include<algorithm>
class Person
{
public:
Person(string name, int age)
{
this->m_Name = name;
this->m_Age = age;
}
string m_Name;
int m_Age;
};
class comparePerosn
{
public:
bool operator()(const Person& p1, const Person& p2)const
{
if (p1.m_Age != p2.m_Age) {//先按年龄排
return p1.m_Age > p2.m_Age;
}//年龄相同按名字排
return p1.m_Name > p2.m_Name;
}
};
void test01()
{
//自定义的数据类型,都会指定排序规则
set<Person, comparePerosn>s;
//创建Person对象
Person p1("张三", 19);
Person p2("李四", 12);
Person p3("王五", 14);
Person p4("赵六", 16);
Person p5("赵七", 16);
s.insert(p1);
s.insert(p2);
s.insert(p3);
s.insert(p4);
s.insert(p5);
for (set<Person, comparePerosn>::iterator it = s.begin(); it != s.end(); it++)
{
cout << "姓名" << (*it).m_Name << "年龄" << it->m_Age << endl;
}
}
int main()
{
test01();
return 0;
}
10,map/multimap容器
1,map基本概念
简介:
map中所有元素都是pair
pair中第一个元素为key(键值),起到索引作用,第二个元素为value(实值)
所有元素都会根据元素的键值自动排序
本质:
map/multimap属于关联式容器,底层结构是用二叉树实现
优点:
可以根据key值快速找到value值
map和multimap区别:
map不允许容器中有重复key值元素.
multimap允许容器中有重复key值元素
2,map构造和赋值
功能描述:
对map容器进行构造和赋值操作
函数原型;
构造:
map<T1,T2> mp;//map默认构造函数:
map( const map &mp);//拷贝构造函数
赋值:
map& operator=(const map &mp);//重载等号操作符
代码图示
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-aAderZDf-1676032966252)(C:\Users\y’z\Desktop\C++\c++核心编程\屏幕截图 2022-12-02 213544.png)]
3,map大小和交换
功能描述:
统计map容器大小以及交换map容器
函数原型:
size();//返回容器中元素的数目
empty();//判断容器是否为空
swsap(st);//交换两个集合容器
代码图示
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-fEVxurax-1676032966252)(C:\Users\y’z\Desktop\C++\c++核心编程\屏幕截图 2022-12-02 214620.png)]
交换代码图示
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-uOrOeTIP-1676032966252)(C:\Users\y’z\Desktop\C++\c++核心编程\屏幕截图 2022-12-02 214704.png)]
总结:
统计大小—size()
判断是否为空— empty()
交换容器— swap()
4,map插入和删除
功能描述:
map容器进行插入数据和删除数据
函数原型:
insert(elem);//在容器中插入元素。
clear();//清除所有元素
erase(pos);//删除pos迭代器所指的元素,返回下一个元素的迭代器。
erase(beg,end);//删除区间[beg,end)的所有元素,返回下一个元素的迭代器。
erase( key);//删除容器中值为key的元素。
代码示例
#define _CRT_SECURE_NO_WARNINGS 1
#include<iostream>
using namespace std;
#include<map>
void PrintMap(map<int, int>& m)
{
for (map<int, int>::iterator it = m.begin(); it != m.end(); it++)
{
cout << "key="<< (*it).first <<" value="<<it->second << endl;
}
cout << endl;
}
void test01()
{
map<int, int>m;
//插入
//第一种
m.insert(pair<int, int>(1, 10));
//第二种
m.insert(make_pair(2, 20));
//第三种
m.insert(map<int, int>::value_type(3, 30));
//第四种
m[4] = 40;
//[]不建议插入,用途 可以利用key访问到value
//cout <<m[4] << endl ;
PrintMap(m);
//删除
m.erase(m.begin());
PrintMap(m);
m.erase(3);//按照key删除
PrintMap(m);
//清空
m.erase(m.begin(), m.end());
//也可用m.clear()
PrintMap(m);
}
int main()
{
test01();
return 0;
}
总结:
map插入方式很多,记住其一即可
插入— insert
删除— erase
清空— clear
5,map查找和统计
功能描述:
对map容器进行查找数据以及统计数据函数原型:
find(key);//查找key是否存在,若存在,返回该键的元素的迭代器;若不存在,返回set.end();
count(key );//统计key的元素个数
代码图示
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-VGt5nVyH-1676032966252)(C:\Users\y’z\Desktop\C++\c++核心编程\屏幕截图 2022-12-03 101506.png)]
总结:
查找—find(返回的是迭代器,无论是否查到都会返回一个迭代器)
统计— count(对于map,结果为0或者1,因为map不允许插入重复key元素,multimap的count统计可能大于1)
6,map容器排序
学习目标:
map容器默认排序规则为按照key值进行从小到大排序,掌握如何改变排序规则
主要技术点:
利用仿函数,可以改变排序规则
代码示例
#define _CRT_SECURE_NO_WARNINGS 1
#include<iostream>
using namespace std;
#include<map>
class Person
{
public:
Person(string name, int age)
{
this->m_Name = name;
this->m_Age = age;
}
public:
string m_Name;
int m_Age;
};
class comparePerson
{
public:
bool operator()(const Person& p1, const Person& p2)const
{
if (p1.m_Age == p2.m_Age)//年龄相当按照姓名排
//不相等按照年龄排
{
return p1.m_Name > p2.m_Name;
}
return p1.m_Age > p2.m_Age;
}
};
void test01()
{
map<Person,int, comparePerson>m1;
Person p1("张三", 19);
Person p2("李四", 12);
Person p3("王五", 14);
Person p4("赵六", 16);
Person p5("赵七", 16);
m1.insert(make_pair(p1, 1));
m1.insert(make_pair(p2, 2));
m1.insert(make_pair(p3, 3));
m1.insert(make_pair(p4, 4));
m1.insert(make_pair(p5, 5));
for (map<Person,int, comparePerson>::iterator it = m1.begin(); it != m1.end(); it++)
{
cout << "姓名" << (*it).first.m_Name << " 年龄" << it->first.m_Age << " value=" << (*it).second << endl;
}
cout << endl;
}
int main()
{
test01();
return 0;
}
总结:
利用仿函数可以指定map容器的排序规则
对于自定义数据类型,map必须要指定排序规则,同set容器
11,案例-员工分组
1,案例描述
公司今天招聘了10个员工(ABCDEFGH),10名员工进入公司之后,需要指派员工在那个部门工作
员工信息有:姓名 工资组成; 部门分为:策划、美术、研发
通过multimap进行信息的插入key(部门编号) value(员工)
分部门显示员工信息
2,实现步骤
1.创建10名员工,放到vector中
2.遍历vector容器,取出每个员工,进行随机分组
3.分组后,将员工部门编号作为key,具体员工作为value,放入到multimap容器中
4.分部门显示员工信息
代码示例
#define _CRT_SECURE_NO_WARNINGS 1
#include<iostream>
using namespace std;
#include<map>
#include<vector>
#include<ctime>
//定义不同部门编号
#define CEHUA 1
#define MEISHU 2
#define YANFA 3
class Person
{
public:
Person(string name, int gozhi)
{
this->m_Name = name;
this->m_Salary = gozhi;
}
public:
string m_Name;
int m_Salary;
};
//创建员工信息
void createPerson(vector<Person>& p)
{
string nameSeed = "ABCDEFGHIJ";
for (int i = 0; i < 10; i++)
{
string name = "员工";
name += nameSeed[i];
int Salary = rand() % 10000 + 10000;
Person per(name, Salary);
p.push_back(per);
}
}
//将员工信息和部门编号结合
void setGroup(vector<Person>&p, multimap<int, Person>&m)
{
for (vector<Person>::iterator it = p.begin(); it != p.end(); it++)
{ //产生随机的部门编号
int deptId = rand() % 3 + 1;
//将员工插入分组
//key部门编号,value具体员工
m.insert(make_pair(deptId, *it));
}
}
//展示员工编号和部门
void showPerson(multimap<int, Person>&m)
{
cout << "策划部门" << endl;
multimap<int,Person>::iterator pos=m.find(CEHUA);//确定策划部门初始位置
int cunt = m.count(CEHUA);//确定策划部门人数
int index = 0;
//因为pos是上面的变量故for循环第一个条件可以不用写
for (; pos != m.end() && index < cunt; pos++, index++)//两个判断条件即策划部门初始位置不
//等于结束位置并且人数不为最后一个
{
cout << pos->second.m_Name << " 工资" << pos->second.m_Salary << endl;
}
cout << endl;
cout << "美术部门" << endl;
pos = m.find(MEISHU);
cunt = m.count(MEISHU);
index = 0;
for (; pos != m.end() && index < cunt; pos++, index++)
{
cout << pos->second.m_Name << " 工资" << pos->second.m_Salary << endl;
}
cout << endl;
cout << "研发部门" << endl;
pos = m.find(YANFA);
cunt = m.count(YANFA);
index = 0;
for (; pos != m.end() && index < cunt; pos++, index++)
{
cout << pos->second.m_Name << " 工资" << pos->second.m_Salary << endl;
}
cout << endl;
}
void test01()
{
srand((unsigned int)time(NULL));//随机数种子
vector<Person>p1;
createPerson(p1);
//员工分组
multimap<int, Person>m1;
setGroup(p1,m1);
//分组显示员工
showPerson(m1);
}
int main()
{
test01();
return 0;
}
4,STL-函数对象
1,函数对象
1,函数对象概念
概念:
重载函数调用操作符的类,其对象常称为函数对象
函数对象使用重载的(时,行为类似函数调用,也叫仿函数
本质:
函数对象(仿函数)是一个类,不是一个函数
2,函数对象使用
特点:
函数对象在使用时,可以像普通函数那样调用,可以有参数,可以有返回值
代码图示
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-P3GQelt4-1676032966253)(C:\Users\y’z\Desktop\C++\c++核心编程\屏幕截图 2022-12-03 203055.png)]
函数对象超出普通函数的概念,函数对象可以有自己的状态
代码图示
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-sE5Mg1Lf-1676032966253)(C:\Users\y’z\Desktop\C++\c++核心编程\屏幕截图 2022-12-03 203231.png)]
函数对象可以作为参数传递
代码图示
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-beBZAzhV-1676032966253)(C:\Users\y’z\Desktop\C++\c++核心编程\屏幕截图 2022-12-03 203322.png)]
2,谓词
1,谓词概念
概念:
返回bool类型的仿函数称为谓词(Pred)
一元谓词
如果operator()接受—个参数,那么叫做—元谓词
代码示例
#define _CRT_SECURE_NO_WARNINGS 1
#include<iostream>
using namespace std;
#include<vector>
#include<algorithm>
class GreateFive
{
public:
bool operator()(int val)
{
return val > 5;
}
};
void test01()
{
vector<int>v;
GreateFive m;
for (int i = 0; i < 10; i++)
{
v.push_back(i);
}
//查找容器中 有没有大于5的数字
//GreateFive()匿名函数对象
//vector<int>::iterator it =find_if(v.begin(), v.end(), GreateFive());
vector<int>::iterator it = find_if(v.begin(), v.end(), m);
//最后一个参数要为函数对象可以自己创建也可以使用匿名函数对象
if (it == v.end())
{
cout << "未找到"<<endl;
}
else
{
for (; it != v.end(); it++)//循环遍历输出
{
cout << *it << endl;
}
}
}
int main()
{
test01();
return 0;
}
结构体代替类也可以
代码图示
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ddB2dZt6-1676032966253)(C:\Users\y’z\Desktop\C++\c++核心编程\屏幕截图 2022-12-03 210837.png)]
**总结:**参数只有一个的谓词,称为—元谓词
二元谓词
如果operator()接受两个参数,那么叫做二元谓词
代码案例
#define _CRT_SECURE_NO_WARNINGS 1
#include<iostream>
using namespace std;
#include<vector>
#include<algorithm>
class MyCompare
{
public:
bool operator()(int v1, int v2)
{
return v1 > v2;
}
};
void test01()
{
vector<int>v;
v.push_back(10);
v.push_back(20);
v.push_back(30);
v.push_back(40);
v.push_back(50);
sort(v.begin(), v.end());
for (vector<int>::iterator it = v.begin(); it != v.end(); it++)
{
cout << *it << " ";
}
cout << endl;
//匿名函数对象MyCompare()
sort(v. begin(), v.end(), MyCompare());
for (vector<int>::iterator it = v.begin(); it != v.end(); it++)
{
cout << *it << " ";
}
cout << endl;
}
int main()
{
test01();
return 0;
}
3,内建函数对象
1,内建函数对象意义
概念:
STL内建了—些函数对象
分类:
算术仿函数
关系仿函数
逻辑仿函数
用法:
这些仿函数所产生的对象,用法和一般函数完全相同
使用内建函数对象,需要引入头文件
#include<functional>
2,算术仿函数
功能描述:
其中negate是一元运算,其他都是二元运算仿
函数原型:
template<class T> T plus<T>//加法仿函数
template<class T> T minus<T>//减法仿函数
template<class T> T multiplies<T>//乘法仿函数
template<class T> T divides<T>//除法仿函数
template<class T> T modulus<T>//取模仿函数
template<class T> T negate<T>//取反仿函数
代码示例
#define _CRT_SECURE_NO_WARNINGS 1
#include<iostream>
using namespace std;
#include<functional>
void test01()
{
//一元仿函数 取反仿函数
negate<int>n;
cout << n(50) << endl;
}
void test02()
{ //二元仿函数 加法仿函数其余类似
plus<int>p;
cout << p(10, 10)<<endl;
}
int main()
{
test01();
test02();
return 0;
}
3,关系仿函数
功能描述:
实现关系对比
仿函数原型:
template<class T> bool equal_to<T>//等于
template<class T> bool not_equal_to<T>//不等于
template<class T> bool greater<T>//大于
template<class T> bool greater_equal<T>//大于等于
template<class T> bool less<T>//小于
template<class T> bool less_equal<T>//小于等于
代码示例
#define _CRT_SECURE_NO_WARNINGS 1
#include<iostream>
using namespace std;
#include<functional>
#include<vector>
#include<algorithm>
class MyCompare
{
public:
bool operator()(int v1, int v2)
{
return v1 > v2;
}
};
void test01()
{
vector<int>v;
v.push_back(10);
v.push_back(30);
v.push_back(40);
v.push_back(20);
v.push_back(50);
for (vector<int>::iterator it = v.begin(); it != v.end(); it++)
{
cout << *it << " ";
}
cout << endl;
//sort(v.begin(), v.end(), MyCompare());
//也可以使用内建函数对象greater<int>()
sort(v.begin(), v.end(), greater<int>());
for (vector<int>::iterator it = v.begin(); it != v.end(); it++)
{
cout << *it << " ";
}
cout << endl;
}
int main()
{
test01();
return 0;
}
**总结:**关系仿函数中最常用的就是greater<>大于
4,逻辑仿函数
功能描述:
实现逻辑运算
函数原型:
template<class T> bool logical_and<T>//逻辑与
template<class T> bool logical_or<T>//逻辑或
template<class T> bool logical_not<T>//逻辑非
代码示例
#define _CRT_SECURE_NO_WARNINGS 1
#include<iostream>
using namespace std;
#include<functional>
#include<vector>
#include<algorithm>
void test01()
{
vector<bool>v;
v.push_back(true);
v.push_back(false);
v.push_back(true);
v.push_back(false);
for (vector<bool>::iterator it = v.begin(); it != v.end(); it++)
{
cout << *it << " ";
}
cout << endl;
//利用逻辑非 将容器v搬运到容器二中,并执行取反操作
vector<bool>v2;
v2.resize(v.size());
transform(v.begin(), v.end(), v2.begin(), logical_not<bool>());
for (vector<bool>::iterator it = v2.begin(); it != v2.end(); it++)
{
cout << *it << " ";
}
cout << endl;
}
int main()
{
test01();
return 0;
}
**总结:**逻辑仿函数实际应用较少,了解即可
5,STL-常用算法
概述:
算法主要是由头文件 组成。
是所有STL头文件中最大的一个,范围涉及到比较、交换、查找、遍历操作、复制、修改等等
体积很小,只包括几个在序列上面进行简单数学运算的模板函数
定义了一些模板类,用以声明函数对象。
1,常用遍历算法
学习目标:
掌握常用的遍历算法
算法简介:
for_each//遍历容器
transform//搬运容器到另—个容器中
1, for_each
功能描述:
实现遍历容器
函数原型:
for_each(iterator beg, iterator end,_func);
遍历算法遍历容器元素
beg开始迭代器
end结束迭代器
_func函数或者函数对象
代码示例
#define _CRT_SECURE_NO_WARNINGS 1
#include<iostream>
using namespace std;
#include<vector>
#include<algorithm>
void Compare(int v)
{
cout << v << " ";
}
class Person
{
public:
void operator()(int v)
{
cout << v << " ";
}
};
void test01()
{
vector<int>v;
for (int i = 0; i < 10; i++)
{
v.push_back(i);
}
for_each(v.begin(), v.end(), Compare);
//普通函数要只要放函数名
cout << endl;
for_each(v.begin(), v.end(), Person());
//仿函数要用函数对象才可以使用
}
int main()
{
test01();
return 0;
}
总结: for_each在实际开发中是最常用遍历算法,需要熟练掌握
2,transform
功能描述:
搬运容器到另一个容器中
函数原型:
transform(iterator beg1, iterator end1,iterator beg2,_func);
beg1源容器开始迭代器
end1源容器结束迭代器
beg2目标容器开始迭代器
_func函数或者函数对象
代码示例
#define _CRT_SECURE_NO_WARNINGS 1
#include<iostream>
using namespace std;
#include<vector>
#include<algorithm>
void Compare(int v)
{
cout << v << " ";
}
class Transform
{
public:
int operator()(int v)
{
return v;
}
};
void test01()
{
vector<int>v;
for (int i = 0; i < 10; i++)
{
v.push_back(i);
}
vector<int>vTarget;//目标容器
vTarget.resize(v.size());//目标容器需要提前开辟空间
transform(v.begin(), v.end(), vTarget.begin(), Transform());
for_each(vTarget.begin(), vTarget.end(), Compare);
cout << endl;
}
int main()
{
test01();
return 0;
}
**总结:**搬这的目标容器必须要提前开辟空间,否则无法正常搬运
2,常用查找算法
学习目标:
掌握常用的查找算法
算法简介:
find//查找元素
find_if//按条件查找元素
adjacdnt_find//查找相邻重复元素
binary_search//二分查找法
count//统计元素个数
count_if//按条件统计元素个数
1,find
功能描述:
直接查找指定元素,找到返回指定元素的迭代器,找不到返回结束迭代器end()
函数原型;
find(iterator beg, iterator end, value) ;
按值查找元素,找到返回指定位置迭代器,找不到返回结束迭代器位置
beg开始迭代器
end结束迭代器
value查找的元素
也可以如下使用
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-AJLlo0dl-1676032966254)(C:\Users\y’z\Desktop\C++\c++核心编程\屏幕截图 2023-01-11 114508.png)]
代码示例
#define _CRT_SECURE_NO_WARNINGS 1
#include<iostream>
using namespace std;
#include<vector>
#include<algorithm>
#include<string>
//查找 内置数据类型
void test01()
{
vector<int>v;
for (int i = 0; i < 10; i++)
{
v.push_back(i);
}
vector<int>::iterator pos = find(v.begin(), v.end(), 5);
if (pos == v. end())
{
cout << "没有找到" << endl;
}
else
{
cout << "找到:" << *pos << endl;
}
}
//查找 自定义数据类型
class Person
{
public:
Person(string name, int age)
{
this->m_Name = name;
this->m_Age = age;
}
bool operator==(const Person &p)
{
if (this->m_Name == p.m_Name && this->m_Age == p.m_Age)
{
return true;
}
return false;
}
public:
string m_Name;
int m_Age;
};
void test02()
{
vector<Person>v;
Person p1("aaa", 10);
Person p2("bbb", 20);
Person p3("ccc", 30);
Person p4("ddd", 40);
v.push_back(p1);
v.push_back(p2);
v.push_back(p3);
v.push_back(p4);
Person pp("bbb", 20);
vector<Person>::iterator it=find(v.begin(), v.end(), pp);
//可以直接使用p2但是再创建一和p2数据一样的更好
if (it == v.end())
{
cout << "没有找到" << endl;
}
else
{
cout << "找到 姓名:" << it->m_Name << " 年龄" << it->m_Age << endl;
}
}
int main()
{
//test01();
test02();
return 0;
}
总结:利用find可以在容器中找指定的元素,返回值是迭代器
2,find_if
功能描述:
按条件查找元素
函数原型:
find_if(iterator beg, iterator end,_pred ) ;
按值查找元素即带条件,找到返回指定位置迭代器,找不到返回结束迭代器位置
beg开始迭代器
end结束迭代器
_Pred 函数或者谓词(返回bool类型的仿函数)
代码示例
#define _CRT_SECURE_NO_WARNINGS 1
#include<iostream>
using namespace std;
#include<vector>
#include<algorithm>
#include<string>
//查找 内置数据类型
class GreaterFiVe
{
public:
bool operator()(int v)
{
return v > 5;
}
};
void test01()
{
vector<int>v;
for (int i = 0; i < 10; i++)
{
v.push_back(i);
}
vector<int>::iterator it =find_if(v.begin(), v.end(), GreaterFiVe());
if (it == v.end())
{
cout << "没有找到" << endl;
}
else
{
cout << "找到了" << *it << endl;
}
}
//查找 自定义数据类型
class Person
{
public:
Person(string name, int age)
{
this->m_Name = name;
this->m_Age = age;
}
//bool operator()(const Person& p)
//{
//return p.m_Age > 20;
//}不能在person类中因为重载()无法分清是仿函数还是构造函数
public:
string m_Name;
int m_Age;
};
class Greater20
{
public:
bool operator()(const Person& p)
{
return p.m_Age > 20;
}
};
void test02()
{
vector<Person>v;
Person p1("aaa", 10);
Person p2("bbb", 20);
Person p3("ccc", 30);
Person p4("ddd", 40);
v.push_back(p1);
v.push_back(p2);
v.push_back(p3);
v.push_back(p4);
//Person pp("bbb", 20);
vector<Person>::iterator it=find_if(v.begin(), v.end(), Greater20());
//可以直接使用p2但是再创建一和p2数据一样的更好
if (it == v.end())
{
cout << "没有找到" << endl;
}
else
{
cout << "找到 姓名:" << it->m_Name << " 年龄" << it->m_Age << endl;
}
}
int main()
{
//test01();
test02();
return 0;
}
3,adjacent_find
功能描述:
查找重复元素
函数原型:
adjacent_find(iterator beg, iterator end);
查找相邻重复元素,返回相邻元素的第一个位置的迭代器
beg开始迭代器
end结束迭代器
代码示例
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-hvz8PqT8-1676032966254)(C:\Users\y’z\Desktop\C++\c++核心编程\屏幕截图 2022-12-04 180428.png)]
4,binary_search
功能描述:
查找指定元素是否存在
函数原型:
bool binary_search(iterator beg, iterator end,value);
查找指定的元素,查到返回true否则false
注意:在无序序列中不可用
beg开始迭代器
end结束迭代器
value查找的元素
代码图示
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-J7Ob59Hf-1676032966254)(C:\Users\y’z\Desktop\C++\c++核心编程\屏幕截图 2022-12-05 192046.png)]
**总结:**二分查找法查找效率很高,值得注意的是查找的容器中元素必须的有序序列
5,count
功能描述:
统计元素个数
函数原型:
count(iterator beg, iterator end, value) ;
统计元素出现次数
beg 开始迭代器
end结束迭代器
value统计的元素
代码示例
#define _CRT_SECURE_NO_WARNINGS 1
#include<iostream>
using namespace std;
#include<vector>
#include<algorithm>
#include<string>
//内置数据类型
void test01()
{
vector<int>v;
v.push_back(10);
v.push_back(20);
v.push_back(30);
v.push_back(30);
v.push_back(40);
int num = count(v.begin(), v.end(), 30);
cout << num << endl;
}
//自定义数据类型
class Person
{
public:
Person(string name, int age)
{
this->m_Name = name;
this->m_Age = age;
}
bool operator==(const Person &p)
{
if (this->m_Age == p.m_Age)
{
return true;
}
else {
return false;
}
}
string m_Name;
int m_Age;
};
void test02()
{
vector<Person>v;
Person p1("张三", 18);
Person p2("李四", 19);
Person p3("王五", 17);
Person p4("赵六", 18);
Person p5("老八", 16);
v.push_back(p1);
v.push_back(p2);
v.push_back(p3);
v.push_back(p4);
v.push_back(p5);
int num=count(v.begin(), v.end(), p4);
cout << num << endl;
}
int main()
{
//test01();
test02();
return 0;
6,count_if
功能描述:
按条件统计元素个数
函数原型:
count_if(iterator beg, iterator end,_pred);
按条件统计元素出现次数
beg开始迭代器
end结束迭代器
_Pred谓词
代码示例
#define _CRT_SECURE_NO_WARNINGS 1
#include<iostream>
using namespace std;
#include<vector>
#include<algorithm>
#include<string>
//内置数据类型
class Greater20
{
public:
bool operator()(int v)
{
return v > 20;
}
};
void test01()
{
vector<int>v;
v.push_back(10);
v.push_back(20);
v.push_back(30);
v.push_back(30);
v.push_back(40);
//Greater20()谓词
int num = count_if(v.begin(), v.end(), Greater20());
cout << num << endl;
}
//自定义数据类型
class Person
{//写在person里面,调用的时候person()编译器不知道是重载还是构造函数,会默认为构造函数,不能匿名构造
public:
Person(string name, int age)
{
this->m_Name = name;
this->m_Age = age;
}
bool operator==(const Person &p)
{
if (this->m_Age == p.m_Age)
{
return true;
}
else {
return false;
}
}
string m_Name;
int m_Age;
};
class AgeGreater20
{
public:
bool operator()(const Person &p)
{
return p.m_Age > 20;
}
};
void test02()
{
vector<Person>v;
Person p1("张三", 18);
Person p2("李四", 21);
Person p3("王五", 17);
Person p4("赵六", 18);
Person p5("老八", 16);
v.push_back(p1);
v.push_back(p2);
v.push_back(p3);
v.push_back(p4);
v.push_back(p5);
int num=count_if(v.begin(), v.end(), AgeGreater20());
cout << num << endl;
}
int main()
{
//test01();
test02();
return 0;
}
注意事项
谓词写在person里面,调用的时候person()编译器不知道是重载还是构造函数,会默认为构造函数,不能匿名构造
3,常用排序算法
学习目标:
掌握常用的排序算法
算法简介:
sort//对容器内元素进行排序
random_shuffle//洗牌指定范围内的元素随机调整次序
merge//容器元素合并,并存储到另一容器中
reverse//反转指定范围的元素
1,sort
功能描述:
对容器内元素进行排序
函数原型:
sort(iterator beg,iterator end,_Pred);
按值查找元素,找到返回指定位置迭代器,找不到返回结束迭代器位置ll beg开始迭代器
end结束迭代器
_Pred谓词
代码示例
#define _CRT_SECURE_NO_WARNINGS 1
#include<iostream>
using namespace std;
#include<algorithm>
#include<vector>
#include<functional>
void test01()
{
vector<int>v;
v.push_back(10);
v.push_back(20);
v.push_back(30);
v.push_back(40);
v.push_back(30);
sort(v.begin(), v.end(), greater<int>());
for (vector<int>::iterator it = v.begin(); it!= v.end(); it++)
{
cout << *it << " ";
}
cout << endl;
}
int main()
{
test01();
return 0;
}
总结: sort属于开发中最常用的算法之一,需熟练掌握
2,random_shuffle
功能描述:
洗牌指定范围内的元素随机调整次序
函数原型:
random_shuffle(iterator beg, iterator end) ;
指定范围内的元素随机调整次序
beg开始迭代器
end结束迭代器
代码图示
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-WjDlzr21-1676032966255)(C:\Users\y’z\Desktop\C++\c++核心编程\屏幕截图 2022-12-05 210922.png)]
总结: random_shuffle洗牌算法比较实用,使用时记得加随机数种子
3,merge
功能描述:
两个容器元素合并,并存储到另一容器中
函数原型:
merge(iterator beg1,iterator end1,iterator beg2,iterator end2,iterator dest);
容器元素合并,并存储到另一容器中
注意:两个容器必须是有序的,合并后也是有序同升序可以同降序不行
beg1容器1开始迭代器
end1容器1结束迭代器
beg2容器2开始迭代器l
end2容器2结束迭代器
dest目标容器开始迭代器
代码图示
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-HuMvKoJx-1676032966255)(C:\Users\y’z\Desktop\C++\c++核心编程\屏幕截图 2022-12-05 211642.png)]
4,reverse
功能描述:
将容器内元素进行反转
预留空间是reserve,reverse是反转
函数原型:
reverse(iterator beg, iterator end);
反转指定范围的元素
beg开始迭代器
end结束迭代器
代码图示
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Bbjx8WL1-1676032966255)(C:\Users\y’z\Desktop\C++\c++核心编程\屏幕截图 2022-12-05 212209.png)]
4,常用拷贝和替换算法
学习目标:
掌握常用的拷贝和替换算法
算法简介:
cop//容器内指定范围的元素拷贝到另一容器中
replace//将容器内指定范围的旧元素修改为新元素
replace_if//容器内指定范围满足条件的元素替换为新元素
swap//互换两个容器的元素
1,copy
功能描述:
容器内指定范围的元素拷贝到另一容器中
函数原型:
copy(iterator beg, iterator end,iterator dest);
按值查找元素,找到返回指定位置迭代器,找不到返回结束迭代器位置
beg 开始迭代器
end 结束迭代器
dest目标起始迭代器
代码图示
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ZYo9cUUc-1676032966255)(C:\Users\y’z\Desktop\C++\c++核心编程\屏幕截图 2022-12-05 213659.png)]
**总结:**利用copy算法在拷贝时,目标容器记得提前开辟空间
2,replace
功能描述:
将容器内指定范围的旧元素修改为新元素
函数原型:
replace(iterator beg,iterator end,oldvalue,newvalue );
将区间内旧元素替换成新元素
beg开始迭代器
end结束迭代器
oldvalue 旧元素
newvalue 新元素
代码图示
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-9ChdBlUW-1676032966256)(C:\Users\y’z\Desktop\C++\c++核心编程\屏幕截图 2022-12-05 214319.png)]
总结: replace会替换区间内满足条件的元素
3,replace_if
功能描述:
将区间内满足条件的元素,替换成指定元素
函数原型:
replace_if(iterator beg, iterator end,_pred,newvalue);
按条件替换元素,满足条件的替换成指定元素
beg开始迭武器
end结束迭代器
_pred谓词
newvalue替换的新元素
代码图示
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-chfOqHi0-1676032966256)(C:\Users\y’z\Desktop\C++\c++核心编程\屏幕截图 2022-12-07 131402.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-xIwwwWHD-1676032966256)(C:\Users\y’z\Desktop\C++\c++核心编程\屏幕截图 2022-12-07 131420.png)]
总结: replace_if按条件查找,可以利用仿函数灵活筛选满足的条件
4,swap
功能描述:
互换两个容器的元素
函数原型:
swap(container c1,container c2);
互换两个容器的元素
c1容器1
c2容器2
代码图示
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-c3g0X6L1-1676032966256)(C:\Users\y’z\Desktop\C++\c++核心编程\屏幕截图 2022-12-07 164537.png)]
总结: swap交换容器时,注意交换的容器要同种类型
5,常用算术生成算法
学习目标:
掌握常用的算术生成算法
注意:
算术生成算法属于小型算法,使用时包含的头文件为
#include <numeric>
算法简介:
accumulate//计算容器元素累计总和·
fill//向容器中添加元素
1,accumulate
功能描述:
计算区间内容器元素累计总和
函数原型:
accumulate(iterator beg, iterator end, value);
计算容器元素累计总和
beg开始迭代器
end结束迭代器
value起始值
代码图示
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Qm7GHWiS-1676032966256)(C:\Users\y’z\Desktop\C++\c++核心编程\屏幕截图 2022-12-07 165233.png)]
**总结:**函数参数3为起始累加值
accumulate使用时头文件注意是numeric,这个算法很实用
2,fill
功能描述:
向容器中填充指定的元素
函数原型:
fill(iterator beg, iterator end, value);
向容器中填充元素
beg开始迭代器
end结束迭代器
value填充的值
代码图示
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-gWssUIpP-1676032966257)(C:\Users\y’z\Desktop\C++\c++核心编程\屏幕截图 2022-12-07 165734.png)]
**总结:**利用fill可以将容器区间内元素填充为指定的值
6,常用集合算法
学习目标:
掌握常用的集合算法
算法简介:
set_intersection//求两个容器的交集
set_union//求两个容器的并集
set_difference//求两个容器的差集
图示
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-PQDY8veb-1676032966257)(C:\Users\y’z\Desktop\C++\c++核心编程\屏幕截图 2022-12-07 180205.png)]
1,set_intersection
功能描述:
求两个容器的交集
函数原型:
set_intersection(iterator beg1,iterator end1,iterator beg2,iterator end2,iterator dest);
求两个集合的交集
注意:两个集合必须是有序序列
beg1容器1开始迭代器
end1容器1结束迭代器
beg2容器2开始迭代器
end2容器2结束迭代器
dest目标容器开始迭代器
代码图示
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-q0FcrTWO-1676032966257)(C:\Users\y’z\Desktop\C++\c++核心编程\屏幕截图 2022-12-07 170709.png)]
总结:
求交集的两个集合必须的有序序列
目标容器开辟空间需要从两个容器中取小值
2,set_union
功能描述:
求两个集合的并集
函数原型:
set_union(iterator beg1,iterator end1,iterator beg2,iterator end2,iterator dest);
求两个集合的并集
set_intersection返回值既是交集中最后一个元素的位置
注意:两个集合必须是有序序列
beg1容器1开始迭代器
end1容器1结束迭代器
beg2容器2开始迭代器
end2容器2结束迭代器
dest目标容器开始迭代器
代码图示
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-BlVHF7oO-1676032966257)(C:\Users\y’z\Desktop\C++\c++核心编程\屏幕截图 2022-12-07 175311.png)]
总结:
求并集的两个集合必须的有序序列
目标容器开辟空间需要两个容器相加
set_union返回值既是并集中最后一个元素的位置
3,set_difference
功能描述:
求两个集合的差集
函数原型:
set_difference(iterator beg1,iterator end1,iterator beg2,iterator end2,iterator dest);
求两个集合的差集
注意:两个集合必须是有序序列
beg1容器1开始迭代器
end1容器1结束迭代器
beg2容器2开始迭代器
end2容器2结束迭代器
dest目标容器开始迭代器
代码图示
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-8AuiOr7S-1676032966258)(C:\Users\y’z\Desktop\C++\c++核心编程\屏幕截图 2022-12-07 180914.png)]
总结:
求差集的两个集合必须的有序序列
目标容器开辟空间需要从两个容器取较大值
set_difference返回值既是差集中最后一个元素的位置
演讲比赛
1,比赛规则
学校举行一场演讲比赛,共有12个人参加。比赛共两轮,第一轮为淘汰赛,第二轮为决赛。
每名选手都有对应的编号,如10001~10012。
比赛方式:分组比赛,每组6个人。
第一轮分为两个小组,整体按照选手编号进行抽签后顺序演讲。
十个评委分别给每名选手打分,去除最高分和最低分,求的平均分为本轮选手的成绩。
当小组演讲完后,淘汰组内排名最后的三个选手,前三名晋级,进入下一轮的比赛。
第二轮为决赛,前三名胜出。
每轮比赛过后需要显示晋级选手的信息。
2,程序功能
开始演讲比赛:完成整届比赛的流程,每个比赛阶段需要给用户一个提示,用户按任意键后继续下一个阶段
查看往届记录:查看之前比赛前三名结果,每次比赛都会记录到文件中,文件用.csv后缀名保存(.csv文件可以保存到Execl文档中)
清空比赛记录:将文件中数据清空
退出比赛程序:可以退出当前程序
3,程序代码示例
1,主程序
依然用while做死循环内置switch选择分支
并在主函数中加随机数种子,当然也可在其他分函数中写但主函数只用写一次
代码示例
#define _CRT_SECURE_NO_WARNINGS 1
#include<iostream>
using namespace std;
#include<string>
#include"speechManager.h"
#include<ctime>;
int main()
{
srand((unsigned int)time(NULL));
SpeechManager sm;
int choice = 0;
while (true)
{
sm.show_Menu();
cout << "请输入你的选择" << endl;
cin >> choice;
switch (choice)
{
case 1://开始比赛
sm.startSpeech();
break;
case 2://查看往届比赛记录
sm.showRecord();
break;
case 3://清空往届比赛记录
sm.clearRecord();
break;
case 0://退出系统
sm.exitSystem();
break;
default:
system("cls");
break;
}
}
system("pause");
return 0;
}
2,SpeechManager.h头文件
首先,这只是一个存放函数名和成员变量的一个类,并不做实现,并且在一个类中写函数和成员变量容器可以灵活的使用不用注意局部变量和全局变量的作用域
代码示例
#pragma once
#define _CRT_SECURE_NO_WARNINGS 1
#include<iostream>
using namespace std;
#include<vector>
#include<map>
#include"speaker.h"
class SpeechManager
{
public:
//构造函数
SpeechManager();
//展示菜单
void show_Menu();
//初始化创建12名选手
void createSpeaker();
//开始比赛-比赛流程控制
void startSpeech();
//抽签
void speechDraw();
//比赛
void speechContest();
//显示比赛结果
void showScore();
//保存分数
void saveRecord();
//读取记录
void loadRecord();
//文件为空的标志
bool fileIsEmpty;
//往届记录
map<int, vector<string>>m_Record;
//key 表示第几届,value记录具体信息
//展示往届得分
void showRecord();
//清空记录
void clearRecord();
//退出系统
void exitSystem();
//初始化属性
void initSpeech();
//析构函数
~SpeechManager();
//比赛选手 容器12人,v1和v2只存放选手的编号
vector<int>v1;
//比赛选手 容器6人
vector<int>v2;
//比赛选手 前三名容器 3人
vector<int>vVictory;
//存放编号 以及对应的 具体选手 容器,int为key后期将v1和v2的编号存放进key当钥匙
map<int, Speaker>m_Speaker;
//记录比赛轮数
int m_Index;
};
3,speaker.h头文件
即创建一个演讲人的类内含姓名,分数其中分数为数组存放两轮分数
代码示例
#pragma once
#define _CRT_SECURE_NO_WARNINGS 1
#include<iostream>
using namespace std;
class Speaker
{
public:
string m_Name;//姓名
double m_Score[2];//分数 两轮
};
4,speakerManager.cpp源文件
即在speakerManager.cpp中实现speakerManager.h中的函数实现
代码示例
#include"speechManager.h"
#include<algorithm>
#include<deque>
#include <numeric>
#include<string>
#include<fstream>
//构造函数
SpeechManager::SpeechManager()
{
//初始化容器和属性
this->initSpeech();
//创建选手
this->createSpeaker();
//获取往届记录
this->loadRecord();
}
//展示菜单
void SpeechManager::show_Menu()
{
cout << "***************************************" << endl;
cout << "***********欢迎参加演讲比赛************" << endl;
cout << "*********** 1,开始演讲比赛 ************" << endl;
cout << "*********** 2,查看演讲记录 ************" << endl;
cout << "*********** 3,清空演讲记录 ************" << endl;
cout << "*********** 0,开始演讲比赛 ************" << endl;
cout << "***************************************" << endl;
cout << endl;
}
//初始化属性
void SpeechManager::initSpeech()
{
//保证容器为空
this->v1.clear();
this->v2.clear();
this->vVictory.clear();
this->m_Speaker.clear();
//初始化比赛轮数
this->m_Index = 1;
//初始化记录容器
this->m_Record.clear();
}
//初始化创建12名选手
void SpeechManager::createSpeaker()
{
string nameSeed = "ABCDEFGHIJKL";
for (int i = 0; i < nameSeed.size(); i++)
{
string name = "选手";
name += nameSeed[i];
Speaker sp;
sp.m_Name = name;
for (int j = 0; j < 2; j++)
{
sp.m_Score[j] = 0;
}
//12名选手编号
this->v1.push_back(i + 10001);
//选手编号 以及对应的选手 存放到map容器中
this->m_Speaker.insert(make_pair(i + 10001, sp));//存入编号和姓名和分数
//后用编号来代表人
}
}
//开始比赛-比赛流程控制
void SpeechManager::startSpeech()
{
//第一轮抽签
this->speechDraw();
//第一轮比赛
this->speechContest();
//显示晋级结果
this->showScore();
//第二轮比赛
this->m_Index++;
//第二轮抽签
this->speechDraw();
//第二轮比赛
this->speechContest();
//显示前三名结果
this->showScore();
//保存比赛结果
this->saveRecord();
//如果比赛完了后查不到记录,因为没有实时更新
//重置往届记录
//初始化属性
this->initSpeech();
//创建选手
this->createSpeaker();
//即更新获取往届记录
this->loadRecord();
cout << "本届比赛完毕" << endl;
system("pause");
system("cls");
}
//抽签
void SpeechManager::speechDraw()
{
cout << "第<<" << this->m_Index << ">>轮比赛选手正在抽签" << endl;
cout << "-----------------------------"<<endl;
cout << "抽签后演讲顺序如下" << endl;
if (this->m_Index == 1)
{
random_shuffle(v1.begin(), v1.end());
for (vector<int>::iterator it = v1.begin(); it != v1.end(); it++)
{
cout << *it << " ";
}
cout << endl;
}
else
{
random_shuffle(v2.begin(), v2.end());
for (vector<int>::iterator it = v2.begin(); it != v2.end(); it++)
{
cout << *it << " ";
}
cout << endl;
}
cout << "-----------------------------" << endl;
system("pause");
system("cls");
}
//比赛
void SpeechManager::speechContest()
{
cout << "-------------第" << this->m_Index << "轮正式比赛开始:-----------"<<endl;
multimap<double, int, greater<double>>groupScore;//临时容器,保存key 分数 value 编号
//multimap可以通过键值key来排序初始为从小到大通过greater<double>可以实现从大到小,实现平均分排序大小
int num = 0;//记录人数
vector<int>v_Src;//比赛的人员容器
if (this->m_Index == 1)
{
v_Src = v1;
}
else
{
v_Src = v2;
}
//遍历所有参赛选手
for (vector<int>::iterator it = v_Src.begin(); it != v_Src.end(); it++)
{
num++;
//评委打分
deque<double>d;
for (int i = 0; i < 10; i++)
{
double score = (rand() % 401 + 600) / 10.f;//600~1000
d.push_back(score);
}
sort(d.begin(), d.end(), greater<double>());//排序使用greater<double>()指定每个分数从大到小在进行尾插,尾删
d.pop_front();//去除最高分
d.pop_back();//去除最低分
double sum = accumulate(d.begin(), d.end(), 0.0f);//总分
double avg = sum / (double)d.size();//平均分
//每个人平均分
this->m_Speaker[*it].m_Score[this->m_Index - 1] = avg;//因为speaker.h中m_Score[2]中只能存放两个元素并且下标索引为0~1故用[this->m_Index-1]
//六个人为一组,用临时容器保存
groupScore.insert(make_pair(avg, *it));
if (num % 6 == 0)
{
cout << "第" << num / 6 << "小组比赛名次:" << endl;
for (multimap<double, int, greater<double>>::iterator it = groupScore.begin(); it != groupScore.end(); it++)
{
cout << "编号" << it->second << "姓名" << this->m_Speaker[it->second].m_Name
<< "成绩" << this->m_Speaker[it->second].m_Score[this->m_Index - 1] << endl;
}
int count = 0;
//取前三名
for (multimap<double, int, greater<double>>::iterator it = groupScore.begin(); it != groupScore.end() && count < 3; it++, count++)
{
if (this->m_Index == 1)
{
v2.push_back((*it).second);
}
else
{
vVictory.push_back((*it).second);
}
}
groupScore.clear();
cout << endl;
}
}
cout << "--------------第" << this->m_Index << "轮比赛完毕:-------------" << endl;
system("pause");
system("cls");
}
void SpeechManager::showScore()
{
cout<< "---------第" << this->m_Index << "轮晋级选手信息如下:---------" << endl;
vector<int>v;
if (this->m_Index == 1)
{
v = v2;//只将编号存放进v中
}
else
{
v = vVictory;
}
for (vector<int>::iterator it = v.begin(); it != v.end(); it++)//用编号来当key取得选手姓名和得分
{
cout << "选手编号" << *it << "姓名" << m_Speaker[*it].m_Name << "选手得分"
<< m_Speaker[*it].m_Score[m_Index - 1] << endl;
}
cout << endl;
system("pause");
system("cls");
this->show_Menu();
}
void SpeechManager::saveRecord()
{
ofstream ofs;
ofs.open("speech.csv", ios::out | ios::app);
//输出的方式打开文件--写文件
//每个人数分数写入到文件中
for (vector<int>::iterator it = vVictory.begin(); it != vVictory.end(); it++)
{
ofs << *it << ","<<m_Speaker[*it].m_Name<<","
<< m_Speaker[*it].m_Score[1] << ",";
//因为为csv格式要用,进行分割
}
//写入了编号,姓名和分数
ofs << endl;//一条信息换行
//关闭文件
ofs.close();
cout << "记录已经保存" << endl;
//更改文件状态
this->fileIsEmpty = false;
}
//读取记录
void SpeechManager::loadRecord()
{
ifstream ifs("speech.csv", ios::in);//读取流对象 读取文件
if (!ifs.is_open())//如果没有找到ifs.is_open()("文件路径".,打开方式)即文件不存在
{
this->fileIsEmpty = true;
//cout << "文件不存在" << endl;
ifs.close();
return;
}
char ch;
ifs >> ch;
//即在文件中读取一个字符判断文件是否读到文件尾即为空
if (ifs.eof())//文件结尾的意思ifs.eof()
{
//cout << "文件为空" << endl;
this->fileIsEmpty = true;
ifs.close();
return;
}
//文件不为空
this->fileIsEmpty = false;
ifs.putback(ch);//将上一个读取的字符读回
string data;
int index = 0;
while (ifs >> data)
{
//cout << data << endl;
vector<string>v;//存放字符串
int pos = -1;//查到“,”位置的变量
int start = 0;//起始位置
while (true)//死循环
{
pos = data.find(",",start);
//利用find将“,”一层一层分割
if (pos == -1)//find()找不到返回-1
{
break;//找不到,则用break返回跳出循环
}
//substr()函数截取字符
string temp = data.substr(start, pos - start);
//找到了,进行分割,参数一 起始位置,参数二 截取长度
v.push_back(temp);//将截取的数据给v容器
start = pos + 1;//起始位置要从pos加一开始找
}
index++;//表示第几届
this->m_Record.insert(make_pair(index, v));
//v.clear();
//再通过speechManager.h中的map容器来获取v中的数据
}
ifs.close();//关闭系统
}
void SpeechManager::showRecord()
{
if (this->fileIsEmpty)//即用fileIsEmpty来判断如果为true即打印下面文字
{
cout << "文件不存在或记录为空" << endl;
}
else
{
for (map<int, vector<string>>::iterator it = m_Record.begin(); it != m_Record.end(); it++)
{//通过迭代器来遍历数据
cout << "第" << it->first << "届" << endl;
cout << "冠军编号 " << it->second[0] << " 姓名 " << it->second[1] << " 分数 " << it->second[2]
<< " 亚军编号 " << it->second[3] << " 姓名 " << it->second[4] << " 分数 " << it->second[5]
<< " 季军编号 " << it->second[6] << " 姓名 " << it->second[7] << " 分数 " << it->second[8]
<< endl;
}
}
system("pause");
system("cls");
}
//清空记录
void SpeechManager::clearRecord()
{
cout << "确定清空?" << endl;
cout << "1,确定" << endl;
cout << "2,返回" << endl;
int select = 0;
cin >> select;
if(select==1)
{
ofstream ofs("speech.csv", ios::trunc);
ofs.clear();
//初始化属性
this->initSpeech();
//创建选手
this->createSpeaker();
//获取往届记录
this->loadRecord();
cout << "清空完成" << endl;
}
system("pause");
system("cls");
}
//退出系统
void SpeechManager::exitSystem()
{
cout << "欢迎下次使用"<<endl;
system("pause");
exit(0);
}
//析构函数
SpeechManager::~SpeechManager()
{
}
机房预约管理系统
案例需求描述
1.1身份简介
分别有三种身份使用该程序
学生代表:申请使用机房
教师:审核学生的预约申请
管理员:给学生、教师创建账号
1.2机房简介
机房总共有3间
1号机房—最大容量20人
2号机房—最多容量50人
3号机房—最多容量100人
1.3申请简介
申请的订单每周由管理员负责清空。
学生可以预约未来一周内的机房使用,预约的日期为周一至周五,预约时
需要选择预约时段(上午、下午)
教师来审核预约,依据实际情况审核预约通过或者不通过
1.4系统具体需求
1,首先进入登录界面,可选登录身份有:
学生代表
老师
管理员
退出
2,每个身份都需要进行验证后,进入子菜单
学生需要输入︰学号、姓名、登录密码
老师需要输入:职工号、姓名、登录密码
管理员需要输入:管理员姓名、登录密码
3,学生具体功能
申请预约—预约机房
查看自身的预约—查看自己的预约状态
查看所有预约—查看全部预约信息以及预约状态
取消预约—取消自身的预约,预约成功或审核中的预约均可取消
注销登录—退出登录
4,教师具体功能
查看所有预约—查看全部预约信息以及预约状态
审核预约—对学生的预约进行审核
注销登录—退出登录
5,管理员具体功能
添加账号―-添加学生或教师的账号,需要检测学生编号或教师职工号是否
重复
查看账号—可以选择查看学生或教师的全部信息
查看机房—查看所有机房的信息
清空预约—清空所有预约记录
注销登录—退出登录
案例实现思路
1,先在主函数中利用while创建死循环,然后再其中使用switch分支判断
进入那个条件,并设置退出系统和输出有误的操作。
2,创建人员头文件并做虚函数,老师头文件与源文件,学生头文件与源
文件,管理员头文件与源文件并在头文件和源文件中创建需求函数并做空
实现,其中,三个头文件继承人员头文件
3,完成主函数与三个源文件的菜单函数,并做老师,学生,管理员三个
有参构造函数,并进行全局文件头文件以及机房类头文件的添加
4,设置登录函数,并以操作文件名和操作身份类型为参数,以读取文件
中的文件来进行登录密码验证
5,分别完成各文件需求函数的书写,先写管理员操作菜单,到学生操作
菜单,最后老师操作菜单
源代码示例
主函数文件
#define _CRT_SECURE_NO_WARNINGS 1
#include<iostream>
#include"globalFile.h"
#include"Identity.h"
#include<fstream>
#include<string>
#include"student.h"
#include"teacher.h"
#include"manager.h"
using namespace std;
void TeacherMenu(Identity*& teacher)
{
while(true)
{
teacher->operMenu();
Teacher* tea = (Teacher*)teacher;
int select = 0;
cin >> select;
switch (select)
{
case 1:
tea->showAllOrMenu();
break;
case 2:
tea->validOrder();
break;
case 0:
cout << "注销成功" << endl;
delete teacher;
system("pause");
system("cls");
return;
}
}
}
void studentMenu(Identity* student)
{
while (true)
{
student->operMenu();
Student* stu = (Student*)student;
int select = 0;
cin >> select;
switch (select)
{
case 1:
stu->applyOrder();
break;
case 2:
stu->showMyOrder();
break;
case 3:
stu->showAllOrder();
break;
case 4:
stu->cancelOrder();
break;
case 0:
cout << "注销成功" << endl;
delete student;
system("pause");
system("cls");
return;
}
}
}
//进入管理员子菜单界面
void managerMenu(Identity*& manager)
{
while (true)
{
//调用管理员子类菜单
manager->operMenu();
Manager* man = (Manager*)manager;//父类强转子类
//Manager* man = new Manager();
int select = 0;
cin >> select;
switch (select)
{
case 1:
man->addPerson();
break;
case 2:
man->showPerson();
break;
case 3:
man->showComputer();
break;
case 4:
man->clearFile();
break;
case 0:
cout << "注销成功" << endl;
delete manager;
system("pause");
system("cls");
return;
}
}
}
//登录功能 参数1,操作文件名 参数2操作身份类型
void LoginIn(string fileName, int type)
{
//父类指针,用于子类对象
Identity* person = NULL;
//读文件
ifstream ifs;
ifs.open(fileName, ios::in);
//文件不出存在
if (!ifs.is_open())
{
cout << "文件不存在" << endl;
ifs.close();
return ;
}
int id = 0;
string name;
string pwd;
if (type == 1)//学生
{
cout << "请输入你的学号" << endl;
cin >> id;
}
else if (type == 2)//老师
{
cout << "请输入你的职工工号" << endl;
cin >> id;
}
cout << "请输入你的姓名" << endl;
cin >> name;
cout << "请输入密码" << endl;
cin >> pwd;
if (type == 1)
{
//学生登录验证
int fId;//文件读取的id号
string fName;//文件读取到的姓名
string fPwd;//文件中的密码
while (ifs >> fId && ifs >> fName && ifs >> fPwd)
{
if (id == fId && name == fName && pwd == fPwd)
{
cout << "学生登录验证成功" << endl;
system("pause");
system("cls");
person = new Student(id, name, pwd);
studentMenu(person);
return;
}
}
}
else if (type == 2)
{
//老师登录验证
int fId;//文件读取的id号
string fName;//文件读取到的姓名
string fPwd;//文件中的密码
while (ifs >> fId && ifs >> fName && ifs >> fPwd)
{
if (id == fId && name == fName && pwd == fPwd)
{
cout << "老师登录验证成功" << endl;
system("pause");
system("cls");
person = new Teacher(id, name, pwd);
TeacherMenu(person);
return;
}
}
}
else if (type == 3)
{
//管理员登录验证
string fName;//文件读取到的姓名
string fPwd;//文件中的密码
while ( ifs >> fName && ifs >> fPwd)
{
if ( name == fName && pwd == fPwd)
{
cout << "管理员登录验证成功" << endl;
system("pause");
system("cls");
person = new Manager(name, pwd);
managerMenu(person);
return;
}
}
}
cout << "验证登录失败" << endl;
system("pause");
system("cls");
return;
}
void show_Menu()
{
cout << "----------------------------" << endl;
cout << "| |" << endl;
cout << "| 请选择你要登录的身份 |" << endl;
cout << "| 1,学生代表 |" << endl;
cout << "| 2,老师 |" << endl;
cout << "| 3,管理员 |" << endl;
cout << "| 0,退出系统 |" << endl;
cout << "| |" << endl;
cout << "----------------------------" << endl;
cout << endl;
}
int main()
{
int choice = 0;
while (true)
{
show_Menu();
cout << "请输入你的选择" << endl;
cin >> choice;
switch (choice)
{
case 1:
LoginIn(STUDENT_FILE, 1);
break;
case 2:
LoginIn(TEACHER_FILE, 2);
break;
case 3:
LoginIn(ADMIN_FILE, 3);
break;
case 0:
cout << "欢迎下一次使用" << endl;
system("pause");
system("cls");
break;
default:
cout << "输入有误" << endl;
system("pause");
system("cls");
break;
}
}
system("pause");
return 0;
}
杂类头文件书写
人员头文件
#pragma once//防止头文件重复包含
#include<iostream>
#include<string>
using namespace std;
//身份抽象类
class Identity
{
public:
virtual void operMenu() = 0;
string m_Name;//用户名
string m_Pwd;//密码
};
机房头文件
#pragma once
#include<iostream>
using namespace std;
//机房类
class ComputerRoom
{
public:
int m_ComId;//机房id
int m_MaxNum;//机房容量
};
全局文件定义头文件
#pragma once
//管理员文件
#define ADMIN_FILE "admin.txt"
//学生文件
#define STUDENT_FILE "student.txt"
//教师文件
#define TEACHER_FILE "teacher.txt"
//机房信息文件
#define COMPUTER_FILE "computerRoom.txt"
//订单文件
#define ORDER_FILE "order.txt"
读取预约并划分头文件与源文件
头文件
#pragma once
#include<iostream>
using namespace std;
#include<map>
#include<fstream>
#include"globalFile.h"
class OrderFile
{
public:
//构造函数
OrderFile();
//更新预约记录
void updateOrder();
//记录的容器 key---记录条数 value--- 具体记录的键值对信息
map<int, map<string, string>>m_orderData;
//记录预约条数
int m_Size;
};
源文件
#define _CRT_SECURE_NO_WARNINGS 1
#include"orderFile.h"
//构造函数
OrderFile::OrderFile()
{
ifstream ifs;
ifs.open(ORDER_FILE, ios::in);
string date;//日期
string interval;//时间段
string stuId;//学生编号
string stuName;//学生姓名
string roomId;//机房编号
string status;//预约状态
this->m_Size = 0;//即开始预约数为0
while (ifs >> date && ifs >> interval && ifs >> stuId && ifs >> stuName && ifs >> roomId && ifs >> status)
{//将每个区块分开来然后对每个区块进行key值和value值的分割
string key;
string value;
map<string, string>m;
int pos = date.find(":");//find如果查找成功则输出查找到的第一个位置,否则返回-1;
if (pos != -1)
{
key = date.substr(0, pos);//substr主要功能是复制子字符串,要求从指定位置开始,并具有指定的长度。
value = date.substr(pos+1, date.size() - pos);//即date这个区块的长度减去查到的分号的长度
m.insert(make_pair(key, value));
}
pos = interval.find(":");
if (pos != -1)
{
key = interval.substr(0, pos);
value = interval.substr(pos + 1, interval.size() - pos);
m.insert(make_pair(key, value));
}
pos = stuId.find(":");
if (pos != -1)
{
key = stuId.substr(0, pos);
value = stuId.substr(pos + 1, stuId.size() - pos);
m.insert(make_pair(key, value));
}
pos = stuName.find(":");
if (pos != -1)
{
key = stuName.substr(0, pos);
value = stuName.substr(pos + 1, stuName.size() - pos);
m.insert(make_pair(key, value));
}
pos = roomId.find(":");
if (pos != -1)
{
key = roomId.substr(0, pos);
value = roomId.substr(pos + 1, roomId.size() - pos);
m.insert(make_pair(key, value));
}
pos = status.find(":");
if (pos != -1)
{
key = status.substr(0, pos);
value = status.substr(pos + 1, status.size() - pos);
m.insert(make_pair(key, value));
}
this->m_orderData.insert(make_pair(this->m_Size, m));
this->m_Size++;
}
ifs.close();
}
//更新预约记录
void OrderFile::updateOrder()
{
if (this->m_Size == 0)
{
return;
}
ofstream ofs(ORDER_FILE, ios::out | ios::trunc);
for (int i = 0; i < m_Size; i++)
{
ofs << "date:" << this->m_orderData[i]["date"] << " ";
ofs << "interval:" << this->m_orderData[i]["interval"] << " ";
ofs << "stuId:" << this->m_orderData[i]["stuId"] << " ";
ofs << "stuName:" << this->m_orderData[i]["stuName"] << " ";
ofs << "roomId:" << this->m_orderData[i]["roomId"] << " ";
ofs << "status:" << this->m_orderData[i]["status"] << " ";
}
ofs.close();
}
管理员头文件与源文件
头文件
#pragma once
#include<iostream>
using namespace std;
#include"Identity.h"
#include"student.h"
#include"teacher.h"
#include"computerRoom.h"
#include<vector>
class Manager :public Identity
{
public:
//默认构造
Manager();
//有参构造
Manager(string name, string pwd);
//选择菜单
virtual void operMenu();
//添加账号
void addPerson();
//初始化容器
void initVector();
//学生容器
vector<Student>vStu;
//老师容器
vector<Teacher>vTea;
vector<ComputerRoom>vCom;
//检测是否有重复 参数:(学号或工号),类型
//返回值(true为存在重复,false为不存在重复)
bool checkRepeat(int id, int type);
//查看账号
void showPerson();
//查看机房信息
void showComputer();
//清空预约记录
void clearFile();
};
源文件
#define _CRT_SECURE_NO_WARNINGS 1
#include"manager.h"
#include"Identity.h"
#include<fstream>
#include"globalFile.h"
//默认构造
Manager::Manager()
{
}
//有参构造
Manager::Manager(string name, string pwd)
{
this->m_Name = name;
this->m_Pwd = pwd;
//初始化容器
this->initVector();
vCom.clear();
ifstream ifs;
ifs.open(COMPUTER_FILE, ios::in);
if (!ifs.is_open())
{
cout << "文件读取失败" << endl;
return;
}
ComputerRoom c;
while (ifs >> c.m_ComId && ifs >> c.m_MaxNum)
{
vCom.push_back(c);
}
//cout << "当前机房数量为" << vCom.size() << endl;
ifs.close();
}
//选择菜单
void Manager::operMenu()
{
cout << "欢迎管理员" << this->m_Name << ",进入系统" << endl;
cout << "-------------------------" << endl;
cout << "| |" << endl;
cout << "| 1,添加账号 |" << endl;
cout << "| 2,查看账号 |" << endl;
cout << "| 3,查看机房 |" << endl;
cout << "| 4,清空预约 |" << endl;
cout << "| 0,注销登录 |" << endl;
cout << "| |" << endl;
cout << "-------------------------" << endl;
cout << endl;
cout << "请输入你想选择的功能" << endl;
}
//添加账号
void Manager::addPerson()
{
cout << "请输入添加账号的类型" << endl;
cout << "1,添加学生" << endl;
cout << "2,添加老师" << endl;
string fileName;
string tip;
ofstream ofs;
int select = 0;
cin >> select;
if (select == 1)
{
fileName = STUDENT_FILE;
tip = "请输入学号";
}
else if(select==2)
{
fileName = TEACHER_FILE;
tip = "请输入职工号";
}
else
{
cout << "输入错误" << endl;
return;
}
ofs.open(fileName, ios::out | ios::app);
int id;
bool shif;
string name;
string pwd;
cout << tip << endl;
while (true)
{
cin >> id;
shif = checkRepeat(id, select);
if (shif == true)
{
if (select == 1)
{
cout << "输入错误,已存在当前学号" << endl;
}
else if (select == 2)
{
cout << "输入错误,已存在当前职工号" << endl;
}
cout << "请重新输入" << endl;
}
else if(shif == false)
{
break;
}
}
cout << "请输入姓名" << endl;
cin >> name;
cout << "请输入密码" << endl;
cin >> pwd;
ofs << id << " " << name << " " << pwd << " " << endl;
cout << "添加成功" << endl;
system("pause");
system("cls");
ofs.close();
this->initVector();
//调用初始化接口,从新获取文件中新的数据
}
void Manager::initVector()
{
vStu.clear();
vTea.clear();
ifstream ifs;
ifs.open(STUDENT_FILE, ios::in);
if (!ifs.is_open())
{
cout << "文件读取失败" << endl;
return;
}
Student s;
while (ifs >> s.m_Id && ifs >> s.m_Name && ifs >> s.m_Pwd)
{
vStu.push_back(s);
}
cout << "当前学生数量为" << vStu.size() << endl;
ifs.close();
ifs.open(TEACHER_FILE, ios::in);
if (!ifs.is_open())
{
cout << "文件读取失败" << endl;
return;
}
Teacher t;
while (ifs >> t.m_EmpId && ifs >> t.m_Name && ifs >> t.m_Pwd)
{
vTea.push_back(t);
}
cout << "当前老师数量为" << vTea.size() << endl;
ifs.close();
}
bool Manager::checkRepeat(int id, int type)//查重函数
{
if (type == 1)
{
for (vector<Student>::iterator it = vStu.begin(); it != vStu.end(); it++)
{
if (id == it->m_Id)
{
return true;
}
}
}
else if (type == 2)
{
for (vector<Teacher>::iterator it = vTea.begin(); it != vTea.end(); it++)
{
if (id == it->m_EmpId)
{
return true;
}
}
}
return false;
}
void printStudent(Student &s)
{
cout << "学号 " << s.m_Id << " 姓名 " << s.m_Name << " 密码 " << s.m_Pwd << endl;
}
void printTeacher(Teacher &t)
{
cout << "职工号 " << t.m_EmpId << " 姓名 " << t.m_Name << " 密码 " << t.m_Pwd << endl;
}
//查看账号
void Manager::showPerson()
{
cout << "请选择你想查看的内容" << endl;
cout << "1,查看所有学生" << endl;
cout << "2,查看所有老师" << endl;
int select;
cin >> select;
if (select == 1)
{
cout << "所有学生信息如下" << endl;
for (int i = 0; i < vStu.size();i++)
{
printStudent(vStu[i]);
}
}
else if (select == 2)
{
cout << "所有老师信息如下" << endl;
for (int i = 0; i < vTea.size(); i++)
{
printTeacher(vTea[i]);
}
}
system("pause");
system("cls");
}
void printComputer(ComputerRoom& c)
{
cout << c.m_ComId << "号机房 容量为 " << c.m_MaxNum << endl;
}
//查看机房信息
void Manager::showComputer()
{
for (int i = 0; i < vCom.size(); i++)
{
printComputer(vCom[i]);
}
system("pause");
system("cls");
}
//清空预约记录
void Manager::clearFile()
{
cout << "您是否确定要清空预约记录吗" << endl;
cout << "1,是" << endl;
cout << "2,否" << endl;
int a;
cin >> a;
if (a == 1)
{
ofstream ofs(ORDER_FILE, ios::trunc);
ofs.close();
cout << "清空成功" << endl;
system("pause");
system("cls");
}
else if (a == 2)
{
cout << "已取消清空操作" << endl;
system("pause");
system("cls");
return;
}
else
{
cout << "输入错误" << endl;
system("pause");
system("cls");
return;
}
}
学生头文件与源文件
头文件
#pragma once
#include<iostream>
using namespace std;
#include<vector>
#include"computerRoom.h"
#include"Identity.h"
#include"orderFile.h"
class Student :public Identity
{
public:
//默认构造
Student();
//有参构造
Student(int id, string name, string pwd);
//菜单界面
virtual void operMenu();
//申请预约
void applyOrder();
vector<ComputerRoom>vCom;
//查看我的预约
void showMyOrder();
//查看所有预约
void showAllOrder();
//取消预约
void cancelOrder();
//学生学号
int m_Id;
};
源文件
#define _CRT_SECURE_NO_WARNINGS 1
#include"student.h"
#include<fstream>
#include"computerRoom.h"
#include"globalFile.h"
//默认构造
Student::Student()
{
}
//有参构造
Student::Student(int id, string name, string pwd)
{
this->m_Id = id;
this->m_Name = name;
this->m_Pwd = pwd;
ifstream ifs;
ifs.open(COMPUTER_FILE, ios::in);
ComputerRoom c;
while (ifs >> c.m_ComId && ifs >> c.m_MaxNum)
{
vCom.push_back(c);
}
ifs.close();
}
//菜单界面
void Student::operMenu()
{
cout << "欢迎学生" << this->m_Name << ",进入系统" << endl;
cout << "-----------------------------" << endl;
cout << "| |" << endl;
cout << "| 1,申请预约 |" << endl;
cout << "| 2,查看自身预约 |" << endl;
cout << "| 3,查看所有预约 |" << endl;
cout << "| 4,取消预约 |" << endl;
cout << "| 0,注销登录 |" << endl;
cout << "| |" << endl;
cout << "-----------------------------" << endl;
cout << endl;
cout << "请输入你想选择的功能" << endl;
}
//申请预约
void Student::applyOrder()
{
cout << "机房开放时间为周一至周五" << endl;
cout << "请选择您要预约的时间" << endl;
cout << "1,周一" << endl;
cout << "2,周二" << endl;
cout << "3,周三" << endl;
cout << "4,周四" << endl;
cout << "5,周五" << endl;
int date=0;
int interval = 0;
int room = 0;
while (true)
{
cin >> date;
if (date >= 1 && date <= 5)
{
break;
}
else
{
cout << "输入有误,请重新输入" << endl;
}
}
cout << "请输入申请预约的时间段" << endl;
cout << "1,上午" << endl;
cout << "2,下午" << endl;
while (true)
{
cin >> interval;
if (interval >= 1 && interval <= 2)
{
break;
}
else
{
cout << "输入有误,请重新输入" << endl;
}
}
cout << "请输入要选择的机房" << endl;
cout << "1号机房,其容量为" << vCom[0].m_MaxNum << endl;
cout << "2号机房,其容量为" << vCom[1].m_MaxNum << endl;
cout << "3号机房,其容量为" << vCom[2].m_MaxNum << endl;
while (true)
{
cin >> room;
if (room >= 1 && room <= 3)
{
break;
}
else
{
cout << "输入有误,请重新输入" << endl;
}
}
cout << "预约成功,审核中" << endl;
ofstream ofs(ORDER_FILE, ios::app);
ofs << "date:" << date << " ";
ofs << "interval:" << interval << " ";
ofs << "stuId:" << this->m_Id << " ";
ofs << "stuName:" << this->m_Name << " ";
ofs << "roomId:" << room << " ";
ofs << "status:" << 1 << endl;//1,表示为审核状态
ofs.close();
system("pause");
system("cls");
}
//查看我的预约
void Student::showMyOrder()
{
OrderFile of;
if (of.m_Size == 0)
{
cout << "无预约记录" << endl;
system("pause");
system("cls");
return;
}
int a = 0;
for (int i = 0; i < of.m_Size; i++)
{
if (atoi(of.m_orderData[i]["stuId"].c_str()) == this->m_Id)
//atoi()函数将数字格式的字符串转换为整数类型。例如,将字符串“12345”转换成数字12345。
//c_str() 函数可以将 const string* 类型 转化为 const char* 类型
{
cout << "预约记录" << ++a << ", ";
cout << "预约日期:周" << of.m_orderData[i]["date"];
//第一个分号为第几条信息,第二个分号为嵌套map中的key即可拿到
//嵌套map的value值
cout << " 预约时段 ";
if (of.m_orderData[i]["interval"] == "1")
{
cout << "上午";
}
else if (of.m_orderData[i]["interval"] == "2")
{
cout << "下午" ;
}
cout << " 预约机房" << of.m_orderData[i]["roomId"];
string status = " 审核状态:";
if (of.m_orderData[i]["status"] == "1")
{
status += "审核中";
}
else if (of.m_orderData[i]["status"] == "2")
{
status += "预约成功";
}
else if (of.m_orderData[i]["status"] == "-1")
{
status += "审核未通过,预约失败";
}
else if (of.m_orderData[i]["status"] == "0")
{
status += "已取消预约";
}
cout << status << endl;
}
}
system("pause");
system("cls");
}
//查看所有预约
void Student::showAllOrder()
{
OrderFile of;
if (of.m_Size == 0)
{
cout << "无预约记录" << endl;
system("pause");
system("cls");
return;
}
for (int i = 0; i < of.m_Size; i++)
{
cout <<"预约记录" << i + 1 << ",";
cout << "预约日期:周" << of.m_orderData[i]["date"];
cout << " 预约时段 ";
if (of.m_orderData[i]["interval"] == "1")
{
cout << "上午";
}
else if (of.m_orderData[i]["interval"] == "2")
{
cout << "下午";
}
cout << " 学号:" << of.m_orderData[i]["stuId"];
cout << " 姓名 " << of.m_orderData[i]["stuName"];
cout << " 预约的机房 " << of.m_orderData[i]["roomId"];
string status = " 审核状态:";
if (of.m_orderData[i]["status"] == "1")
{
status += "审核中";
}
else if (of.m_orderData[i]["status"] == "2")
{
status += "预约成功";
}
else if (of.m_orderData[i]["status"] == "-1")
{
status += "审核未通过,预约失败";
}
else if (of.m_orderData[i]["status"] == "0")
{
status += "已取消预约";
}
cout << status << endl;
}
system("pause");
system("cls");
}
//取消预约
void Student::cancelOrder()
{
OrderFile of;
if (of.m_Size == 0)
{
cout << "无预约记录" << endl;
system("pause");
system("cls");
return;
}
cout << "可以取消审核中或审核成功的预约" << endl;
vector<int>v;
int index = 0;
for (int i = 0; i < of.m_Size; i++)
{
if (atoi(of.m_orderData[i]["stuId"].c_str()) == this->m_Id)
//atoi()函数将数字格式的字符串转换为整数类型。例如,将字符串“12345”转换成数字12345。
//c_str() 函数可以将 const string* 类型 转化为 const char* 类型
{
if(of.m_orderData[i]["status"]=="1"||of.m_orderData[i]["status"]=="2")
{
v.push_back(i);
cout << "预约记录" << ++index << ", ";
cout << "预约日期:周" << of.m_orderData[i]["date"];
//第一个分号为第几条信息,第二个分号为嵌套map中的key即可拿到
//嵌套map的value值
cout << " 预约时段 ";
if (of.m_orderData[i]["interval"] == "1")
{
cout << "上午";
}
else if (of.m_orderData[i]["interval"] == "2")
{
cout << "下午";
}
cout << " 预约机房" << of.m_orderData[i]["roomId"];
string status = " 审核状态:";
if (of.m_orderData[i]["status"] == "1")
{
status += "审核中";
}
else if (of.m_orderData[i]["status"] == "2")
{
status += "预约成功";
}
else if (of.m_orderData[i]["status"] == "-1")
{
status += "审核未通过,预约失败";
}
else if (of.m_orderData[i]["status"] == "0")
{
status += "已取消预约";
}
cout << status << endl;
}
}
}
cout << "请输入想要的取消记录,按0即为返回" << endl;
int select = 0;
while (true)
{
cin >> select;
if (select >= 0 && select <= v.size())
{
if (select == 0)
{
break;
}
else
{
of.m_orderData[v[select - 1]]["status"] = "0";
//例vector容器中存放了0,1,3如果我想取消3则select输入3
//select - 1,得到2,vector中的数据按0,1,2排序,2则得到4的数据
of.updateOrder();
cout << "已经取消预约" << endl;
break;
}
}
cout << "输入有误,请重新输入" << endl;
}
system("pause");
system("cls");
}
老师头文件与源文件
头文件
#pragma once
#include<iostream>
using namespace std;
#include"Identity.h"
class Teacher :public Identity
{
public:
//默认构造
Teacher();
//有参构造
Teacher(int empId, string name, string pwd);
//教师菜单
virtual void operMenu();
//查看所有预约
void showAllOrMenu();
//审核所有预约
void validOrder();
int m_EmpId;//教师编号
};
源文件
#define _CRT_SECURE_NO_WARNINGS 1
#include"teacher.h"
#include"orderFile.h"
#include<vector>
//默认构造
Teacher::Teacher()
{
}
//有参构造
Teacher::Teacher(int empId, string name, string pwd)
{
//初始化属性
this->m_EmpId = empId;
this->m_Name = name;
this->m_Pwd = pwd;
}
//教师菜单
void Teacher::operMenu()
{
cout << "欢迎老师" << this->m_Name << ",进入系统" << endl;
cout << "-----------------------------" << endl;
cout << "| |" << endl;
cout << "| 1,查看所有预约 |" << endl;
cout << "| 2,审核预约 |" << endl;
cout << "| 0,注销登录 |" << endl;
cout << "| |" << endl;
cout << "-----------------------------" << endl;
cout << endl;
cout << "请输入你想要的选择功能"<<endl;
}
//查看所有预约
void Teacher::showAllOrMenu()
{
OrderFile of;
if (of.m_Size == 0)
{
cout << "无预约记录" << endl;
system("pause");
system("cls");
return;
}
for (int i = 0; i < of.m_Size; i++)
{
cout << "预约记录" << i + 1 << ",";
cout << "预约日期:周" << of.m_orderData[i]["date"];
cout << " 预约时段 ";
if (of.m_orderData[i]["interval"] == "1")
{
cout << "上午";
}
else if (of.m_orderData[i]["interval"] == "2")
{
cout << "下午";
}
cout << " 学号:" << of.m_orderData[i]["stuId"];
cout << " 姓名 " << of.m_orderData[i]["stuName"];
cout << " 预约的机房 " << of.m_orderData[i]["roomId"];
string status = " 审核状态:";
if (of.m_orderData[i]["status"] == "1")
{
status += "审核中";
}
else if (of.m_orderData[i]["status"] == "2")
{
status += "预约成功";
}
else if (of.m_orderData[i]["status"] == "-1")
{
status += "审核未通过,预约失败";
}
else if (of.m_orderData[i]["status"] == "0")
{
status += "已取消预约";
}
cout << status << endl;
}
system("pause");
system("cls");
}
//审核所有预约
void Teacher::validOrder()
{
OrderFile of;
if (of.m_Size == 0)
{
cout << "无预约记录" << endl;
system("pause");
system("cls");
return;
}
cout << "当前学生待审核的预约列表如下" << endl;
vector<int>v;
int index = 0;
for (int i = 0; i < of.m_Size; i++)
{
if (of.m_orderData[i]["status"] == "1" )
{
v.push_back(i);
cout << "预约记录" << ++index << ", ";
cout << "预约日期:周" << of.m_orderData[i]["date"];
//第一个分号为第几条信息,第二个分号为嵌套map中的key即可拿到
//嵌套map的value值
cout << " 预约时段 ";
if (of.m_orderData[i]["interval"] == "1")
{
cout << "上午";
}
else if (of.m_orderData[i]["interval"] == "2")
{
cout << "下午";
}
cout << " 学号:" << of.m_orderData[i]["stuId"];
cout << " 姓名 " << of.m_orderData[i]["stuName"];
cout << " 预约机房" << of.m_orderData[i]["roomId"];
string status = " 审核状态:";
if (of.m_orderData[i]["status"] == "1")
{
status += "审核中";
}
else if (of.m_orderData[i]["status"] == "2")
{
status += "预约成功";
}
else if (of.m_orderData[i]["status"] == "-1")
{
status += "审核未通过,预约失败";
}
else if (of.m_orderData[i]["status"] == "0")
{
status += "已取消预约";
}
cout << status << endl;
}
}
int b;
cout << "请输入想要审核的预约序号,按0即为返回" << endl;
int select = 0;
while (true)
{
cin >> select;
if (select >= 0 && select <= v.size())
{
if (select == 0)
{
break;
}
else
{
cout << "请输入审核结果" << endl;
cout << "1,同意预约" << endl;
cout << "2,拒绝预约" << endl;
cin >> b;
if (b == 1)
{
of.m_orderData[v[select - 1]]["status"] = "2";
}
else if (b == 2)
{
of.m_orderData[v[select - 1]]["status"] = "-1";
}
//例vector容器中存放了0,1,3如果我想取消3则select输入3
//select - 1,得到2,vector中的数据按0,1,2排序,2则得到4的数据
of.updateOrder();
cout << "已同意预约" << endl;
break;
}
}
cout << "输入有误,请重新输入" << endl;
}
system("pause");
system("cls");
}
;
}
else if (a == 2)
{
cout << “已取消清空操作” << endl;
system(“pause”);
system(“cls”);
return;
}
else
{
cout << “输入错误” << endl;
system(“pause”);
system(“cls”);
return;
}
}
#### 学生头文件与源文件
**头文件**
```c++
#pragma once
#include<iostream>
using namespace std;
#include<vector>
#include"computerRoom.h"
#include"Identity.h"
#include"orderFile.h"
class Student :public Identity
{
public:
//默认构造
Student();
//有参构造
Student(int id, string name, string pwd);
//菜单界面
virtual void operMenu();
//申请预约
void applyOrder();
vector<ComputerRoom>vCom;
//查看我的预约
void showMyOrder();
//查看所有预约
void showAllOrder();
//取消预约
void cancelOrder();
//学生学号
int m_Id;
};
源文件
#define _CRT_SECURE_NO_WARNINGS 1
#include"student.h"
#include<fstream>
#include"computerRoom.h"
#include"globalFile.h"
//默认构造
Student::Student()
{
}
//有参构造
Student::Student(int id, string name, string pwd)
{
this->m_Id = id;
this->m_Name = name;
this->m_Pwd = pwd;
ifstream ifs;
ifs.open(COMPUTER_FILE, ios::in);
ComputerRoom c;
while (ifs >> c.m_ComId && ifs >> c.m_MaxNum)
{
vCom.push_back(c);
}
ifs.close();
}
//菜单界面
void Student::operMenu()
{
cout << "欢迎学生" << this->m_Name << ",进入系统" << endl;
cout << "-----------------------------" << endl;
cout << "| |" << endl;
cout << "| 1,申请预约 |" << endl;
cout << "| 2,查看自身预约 |" << endl;
cout << "| 3,查看所有预约 |" << endl;
cout << "| 4,取消预约 |" << endl;
cout << "| 0,注销登录 |" << endl;
cout << "| |" << endl;
cout << "-----------------------------" << endl;
cout << endl;
cout << "请输入你想选择的功能" << endl;
}
//申请预约
void Student::applyOrder()
{
cout << "机房开放时间为周一至周五" << endl;
cout << "请选择您要预约的时间" << endl;
cout << "1,周一" << endl;
cout << "2,周二" << endl;
cout << "3,周三" << endl;
cout << "4,周四" << endl;
cout << "5,周五" << endl;
int date=0;
int interval = 0;
int room = 0;
while (true)
{
cin >> date;
if (date >= 1 && date <= 5)
{
break;
}
else
{
cout << "输入有误,请重新输入" << endl;
}
}
cout << "请输入申请预约的时间段" << endl;
cout << "1,上午" << endl;
cout << "2,下午" << endl;
while (true)
{
cin >> interval;
if (interval >= 1 && interval <= 2)
{
break;
}
else
{
cout << "输入有误,请重新输入" << endl;
}
}
cout << "请输入要选择的机房" << endl;
cout << "1号机房,其容量为" << vCom[0].m_MaxNum << endl;
cout << "2号机房,其容量为" << vCom[1].m_MaxNum << endl;
cout << "3号机房,其容量为" << vCom[2].m_MaxNum << endl;
while (true)
{
cin >> room;
if (room >= 1 && room <= 3)
{
break;
}
else
{
cout << "输入有误,请重新输入" << endl;
}
}
cout << "预约成功,审核中" << endl;
ofstream ofs(ORDER_FILE, ios::app);
ofs << "date:" << date << " ";
ofs << "interval:" << interval << " ";
ofs << "stuId:" << this->m_Id << " ";
ofs << "stuName:" << this->m_Name << " ";
ofs << "roomId:" << room << " ";
ofs << "status:" << 1 << endl;//1,表示为审核状态
ofs.close();
system("pause");
system("cls");
}
//查看我的预约
void Student::showMyOrder()
{
OrderFile of;
if (of.m_Size == 0)
{
cout << "无预约记录" << endl;
system("pause");
system("cls");
return;
}
int a = 0;
for (int i = 0; i < of.m_Size; i++)
{
if (atoi(of.m_orderData[i]["stuId"].c_str()) == this->m_Id)
//atoi()函数将数字格式的字符串转换为整数类型。例如,将字符串“12345”转换成数字12345。
//c_str() 函数可以将 const string* 类型 转化为 const char* 类型
{
cout << "预约记录" << ++a << ", ";
cout << "预约日期:周" << of.m_orderData[i]["date"];
//第一个分号为第几条信息,第二个分号为嵌套map中的key即可拿到
//嵌套map的value值
cout << " 预约时段 ";
if (of.m_orderData[i]["interval"] == "1")
{
cout << "上午";
}
else if (of.m_orderData[i]["interval"] == "2")
{
cout << "下午" ;
}
cout << " 预约机房" << of.m_orderData[i]["roomId"];
string status = " 审核状态:";
if (of.m_orderData[i]["status"] == "1")
{
status += "审核中";
}
else if (of.m_orderData[i]["status"] == "2")
{
status += "预约成功";
}
else if (of.m_orderData[i]["status"] == "-1")
{
status += "审核未通过,预约失败";
}
else if (of.m_orderData[i]["status"] == "0")
{
status += "已取消预约";
}
cout << status << endl;
}
}
system("pause");
system("cls");
}
//查看所有预约
void Student::showAllOrder()
{
OrderFile of;
if (of.m_Size == 0)
{
cout << "无预约记录" << endl;
system("pause");
system("cls");
return;
}
for (int i = 0; i < of.m_Size; i++)
{
cout <<"预约记录" << i + 1 << ",";
cout << "预约日期:周" << of.m_orderData[i]["date"];
cout << " 预约时段 ";
if (of.m_orderData[i]["interval"] == "1")
{
cout << "上午";
}
else if (of.m_orderData[i]["interval"] == "2")
{
cout << "下午";
}
cout << " 学号:" << of.m_orderData[i]["stuId"];
cout << " 姓名 " << of.m_orderData[i]["stuName"];
cout << " 预约的机房 " << of.m_orderData[i]["roomId"];
string status = " 审核状态:";
if (of.m_orderData[i]["status"] == "1")
{
status += "审核中";
}
else if (of.m_orderData[i]["status"] == "2")
{
status += "预约成功";
}
else if (of.m_orderData[i]["status"] == "-1")
{
status += "审核未通过,预约失败";
}
else if (of.m_orderData[i]["status"] == "0")
{
status += "已取消预约";
}
cout << status << endl;
}
system("pause");
system("cls");
}
//取消预约
void Student::cancelOrder()
{
OrderFile of;
if (of.m_Size == 0)
{
cout << "无预约记录" << endl;
system("pause");
system("cls");
return;
}
cout << "可以取消审核中或审核成功的预约" << endl;
vector<int>v;
int index = 0;
for (int i = 0; i < of.m_Size; i++)
{
if (atoi(of.m_orderData[i]["stuId"].c_str()) == this->m_Id)
//atoi()函数将数字格式的字符串转换为整数类型。例如,将字符串“12345”转换成数字12345。
//c_str() 函数可以将 const string* 类型 转化为 const char* 类型
{
if(of.m_orderData[i]["status"]=="1"||of.m_orderData[i]["status"]=="2")
{
v.push_back(i);
cout << "预约记录" << ++index << ", ";
cout << "预约日期:周" << of.m_orderData[i]["date"];
//第一个分号为第几条信息,第二个分号为嵌套map中的key即可拿到
//嵌套map的value值
cout << " 预约时段 ";
if (of.m_orderData[i]["interval"] == "1")
{
cout << "上午";
}
else if (of.m_orderData[i]["interval"] == "2")
{
cout << "下午";
}
cout << " 预约机房" << of.m_orderData[i]["roomId"];
string status = " 审核状态:";
if (of.m_orderData[i]["status"] == "1")
{
status += "审核中";
}
else if (of.m_orderData[i]["status"] == "2")
{
status += "预约成功";
}
else if (of.m_orderData[i]["status"] == "-1")
{
status += "审核未通过,预约失败";
}
else if (of.m_orderData[i]["status"] == "0")
{
status += "已取消预约";
}
cout << status << endl;
}
}
}
cout << "请输入想要的取消记录,按0即为返回" << endl;
int select = 0;
while (true)
{
cin >> select;
if (select >= 0 && select <= v.size())
{
if (select == 0)
{
break;
}
else
{
of.m_orderData[v[select - 1]]["status"] = "0";
//例vector容器中存放了0,1,3如果我想取消3则select输入3
//select - 1,得到2,vector中的数据按0,1,2排序,2则得到4的数据
of.updateOrder();
cout << "已经取消预约" << endl;
break;
}
}
cout << "输入有误,请重新输入" << endl;
}
system("pause");
system("cls");
}
老师头文件与源文件
头文件
#pragma once
#include<iostream>
using namespace std;
#include"Identity.h"
class Teacher :public Identity
{
public:
//默认构造
Teacher();
//有参构造
Teacher(int empId, string name, string pwd);
//教师菜单
virtual void operMenu();
//查看所有预约
void showAllOrMenu();
//审核所有预约
void validOrder();
int m_EmpId;//教师编号
};
源文件
#define _CRT_SECURE_NO_WARNINGS 1
#include"teacher.h"
#include"orderFile.h"
#include<vector>
//默认构造
Teacher::Teacher()
{
}
//有参构造
Teacher::Teacher(int empId, string name, string pwd)
{
//初始化属性
this->m_EmpId = empId;
this->m_Name = name;
this->m_Pwd = pwd;
}
//教师菜单
void Teacher::operMenu()
{
cout << "欢迎老师" << this->m_Name << ",进入系统" << endl;
cout << "-----------------------------" << endl;
cout << "| |" << endl;
cout << "| 1,查看所有预约 |" << endl;
cout << "| 2,审核预约 |" << endl;
cout << "| 0,注销登录 |" << endl;
cout << "| |" << endl;
cout << "-----------------------------" << endl;
cout << endl;
cout << "请输入你想要的选择功能"<<endl;
}
//查看所有预约
void Teacher::showAllOrMenu()
{
OrderFile of;
if (of.m_Size == 0)
{
cout << "无预约记录" << endl;
system("pause");
system("cls");
return;
}
for (int i = 0; i < of.m_Size; i++)
{
cout << "预约记录" << i + 1 << ",";
cout << "预约日期:周" << of.m_orderData[i]["date"];
cout << " 预约时段 ";
if (of.m_orderData[i]["interval"] == "1")
{
cout << "上午";
}
else if (of.m_orderData[i]["interval"] == "2")
{
cout << "下午";
}
cout << " 学号:" << of.m_orderData[i]["stuId"];
cout << " 姓名 " << of.m_orderData[i]["stuName"];
cout << " 预约的机房 " << of.m_orderData[i]["roomId"];
string status = " 审核状态:";
if (of.m_orderData[i]["status"] == "1")
{
status += "审核中";
}
else if (of.m_orderData[i]["status"] == "2")
{
status += "预约成功";
}
else if (of.m_orderData[i]["status"] == "-1")
{
status += "审核未通过,预约失败";
}
else if (of.m_orderData[i]["status"] == "0")
{
status += "已取消预约";
}
cout << status << endl;
}
system("pause");
system("cls");
}
//审核所有预约
void Teacher::validOrder()
{
OrderFile of;
if (of.m_Size == 0)
{
cout << "无预约记录" << endl;
system("pause");
system("cls");
return;
}
cout << "当前学生待审核的预约列表如下" << endl;
vector<int>v;
int index = 0;
for (int i = 0; i < of.m_Size; i++)
{
if (of.m_orderData[i]["status"] == "1" )
{
v.push_back(i);
cout << "预约记录" << ++index << ", ";
cout << "预约日期:周" << of.m_orderData[i]["date"];
//第一个分号为第几条信息,第二个分号为嵌套map中的key即可拿到
//嵌套map的value值
cout << " 预约时段 ";
if (of.m_orderData[i]["interval"] == "1")
{
cout << "上午";
}
else if (of.m_orderData[i]["interval"] == "2")
{
cout << "下午";
}
cout << " 学号:" << of.m_orderData[i]["stuId"];
cout << " 姓名 " << of.m_orderData[i]["stuName"];
cout << " 预约机房" << of.m_orderData[i]["roomId"];
string status = " 审核状态:";
if (of.m_orderData[i]["status"] == "1")
{
status += "审核中";
}
else if (of.m_orderData[i]["status"] == "2")
{
status += "预约成功";
}
else if (of.m_orderData[i]["status"] == "-1")
{
status += "审核未通过,预约失败";
}
else if (of.m_orderData[i]["status"] == "0")
{
status += "已取消预约";
}
cout << status << endl;
}
}
int b;
cout << "请输入想要审核的预约序号,按0即为返回" << endl;
int select = 0;
while (true)
{
cin >> select;
if (select >= 0 && select <= v.size())
{
if (select == 0)
{
break;
}
else
{
cout << "请输入审核结果" << endl;
cout << "1,同意预约" << endl;
cout << "2,拒绝预约" << endl;
cin >> b;
if (b == 1)
{
of.m_orderData[v[select - 1]]["status"] = "2";
}
else if (b == 2)
{
of.m_orderData[v[select - 1]]["status"] = "-1";
}
//例vector容器中存放了0,1,3如果我想取消3则select输入3
//select - 1,得到2,vector中的数据按0,1,2排序,2则得到4的数据
of.updateOrder();
cout << "已同意预约" << endl;
break;
}
}
cout << "输入有误,请重新输入" << endl;
}
system("pause");
system("cls");
}