快捷键
shift+tab 整体缩进
转到定义后再返回:Ctrl+shift+8
sizeof关键字
作用:利用sizeof关键字可以统计数据类型所占用的内存大小
语法:sizeof(数据类型/变量)
科学计数法
float f1=3e2;//300
float f2=3e-2;//0.03
字符型变量对应ASCII码
a-97
A-65
水平制表符 \t 作用:整齐的输出数据
字符串
1、c风格字符串: char 变量名[]=“字符串值”
注意事项 1、char 字符串名 [] 2、等号后面 要用双引号 包含起来字符串
2、c++风格字符串: string 变量名=“字符串值”
注意事项:包含一个头文件
布尔类型
只有两个值 (只要非0就是真) 占一个字节
本质: 1代表真 0代表假
注释快捷键:Ctrl+k+c
运算符
两个整数相除,结果依然是整数,将小数部分去除
两个数字相除,除数不可以为0,所以也做不了取模运算
两个小数不可以做取模运算(只有整型数据可以做取模运算)
程序流程结构
三种程序运行结构:顺序,选择,循环结构
三目运算符
表达式1 ?表达式2:表达式3
注意:在c++中三目运算符返回的是变量,可以继续赋值
switch
switch(表达式)
{
case 结果1:执行语句;break;
case 结果2:执行语句;break;
…
default:执行语句;break;
}
注意:1、switch语句中表达式只能是整型或者字符型
2、case里如果没有break,那么程序会一直向下执行
3、缺点是不能判断空间
while循环语句
while(循环条件){循环语句}
注意:在写循环时一定要避免死循环的出现
添加随机数种子 作用:利用当前系统时间生成随机数,防止每次随机数都一样
srand((unsigned int)time(NULL));//写在main函数中
需要包含头文件
生成随机数的函数 rand()
rand()%100+1生成一个0+1~99+1的随机数
可以使用break关键字来退出当前循环
do…while循环语句
语法:do{循环语句}while(循环条件);
跳转语句
1、break语句
作用:用于跳出选择结构或者循环结构
使用时机:
1、出现在switch语句中,作用是终止case并跳出switch
2、出现在循环语句中,作用是跳出当前的循环语句
3、出现在嵌套循环中,跳出最近的内层循环语句
2、continue语句
作用:在循环语句中,跳过本次循环中余下尚未执行的语句,继续执行下一次循环
3、goto语句
语法: goto 标记;
标记:
解释:如果标记的名称存在,执行到goto语句时,会跳转到标记的位置
数组
特点:放在一块连续的内存空间中
数组中的每个元素都是相同的数据类型
注意:数组元素的下标是从0开始的
在初始化时没有全部填写完,会用0来填补
一维数组名的用途:
1、可以统计整个数组在内存中的长度
2、可以获取数组在内存中的首地址
#include<iostream>
using namespace std;
int main()
{
int arr[5] = { 1,2,3,4,5 };
cout << "数组的地址是: " << (int)arr << endl;
cout << "数组的首地址是: " << (int)&arr[0] << endl;
return 0;
}
注意:数组名是常量,不可以进行赋值操作
一维数组逆序输出代码
#include<iostream>
using namespace std;
int main()
{
int arr[5] = { 300,350,200,400,250 };
cout << "逆序前的数组为:" << endl;
for (int i = 0; i < 5; i++)
{
cout << arr[i] << " ";
}
cout << endl;
int start = 0;
int end = sizeof(arr) / sizeof(arr[0]) - 1;
int temp = 0;
while (start < end)
{
temp = arr[start];
arr[start] = arr[end];
arr[end] = temp;
end--;
start++;
}
cout << "逆序后的数组为:" << endl;
for (int i = 0; i < 5; i++)
{
cout << arr[i] << " ";
}
cout << endl;
return 0;
}
冒泡排序
总共排序轮数:元素个数-1
内层循环对比次数=元素个数-当前轮数-1
冒泡排序代码
#include<iostream>
using namespace std;
int main()
{
int arr[9] = { 1,3,8,4,6,2,9,7,5 };
int count = sizeof(arr) / sizeof(arr[0]);
cout << "排序前元素顺序为: " << endl;
for (int i = 0; i < count; i++)
{
cout << arr[i] << " ";
}
cout << endl;
for (int i = 0; i < count - 1; i++)
{
int temp = 0;
for (int j = 0; j < count - i - 1; j++)
{
if (arr[j] > arr[j + 1])
{
temp = arr[j];
arr[j] = arr[j + 1];
arr[j + 1] = temp;
}
}
}
cout << "排序后元素顺序为: " << endl;
for (int i = 0; i < count; i++)
{
cout << arr[i] << " ";
}
cout << endl;
return 0;
}
二维数组
二维数组名作用:
1、查看二维数组所占内存空间
sizeof(arr[0])//得到数组arr第一行的内存空间
2、获取二维数组首地址
函数
5个要素:
1、返回值类型
2、函数名
3、参数列表
4、函数体语句
5、return语句
语法:
返回值类型 函数名( 参数列表)
{
函数体语句;
return语句;
}
注意:
1、如果函数不需要返回值,声明的时候可以写void
2、当我们做值传递的时候,函数的形参发生改变,并不会影响实参
3、函数的声明可以多次,但是定义只能一次
4、函数的声明可以提前告诉编译器函数的存在
函数的分文件编写
作用:让代码更加清晰
4个步骤:
1、创建后缀名为.h的头文件
2、创建后缀名为.cpp的源文件
3、在头文件中写函数的声明
4、在源文件中写函数的定义
指针
作用:可以通过指针间接访问内存
定义语法:
数据类型 *指针变量名;
可以通过解引用的方式来找到指针指向的内存
指针前加 * 代表解引用,找到指针指向的内存中的数据
不论数据类型,
在32位操作系统下,指针占用4个字节
在64位操作系统下,指针占用8个字节
空指针:指针变量指向内存中编号为0的空间
作用:初始化指针变量
注意:空指针指向的内存是不可以访问的
0~255之间的内存编号是系统占用的,因此不可以访问
野指针:指针变量指向非法的内存空间
在程序中要尽量避免
int *p =(int *)0x1100;//野指针
注意:
空指针和野指针都不是我们自己申请的空间,因此不要访问
const修饰指针
三种情况:
1、const修饰指针—常量指针
const int*p=&a;
特点:指针的指向可以修改,但是指针指向的值不可以改
2、const修饰常量—指针常量
特点:指针指向的值可以改,指针的指向不可以改
int *const p=&a;
3、const既修饰指针,又修饰常量
const int *const p=&a;
特点:指针的指向和指向的值都不可以改变
用指针访问数组
#include<iostream>
using namespace std;
int main()
{
int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };
int* p = arr;//arr就是数组首地址
cout << "利用指针访问第二个元素:" << *p << endl;
p++;//让指针向后偏移四个字节
cout << "利用指针访问第二个元素:" << *p << endl;
cout << "利用指针来遍历数组:" << endl;
int* p2 = arr;
for (int i = 0; i < 10; i++)
{
cout << *p2 << " ";
p2++;
}
cout << endl;
return 0;
}
结构体
属于用户自定义的数据类型,允许用户存储不同的数据类型
语法:
struct 结构体名{结构体成员列表};
创建变量方式:(在c++中创建变量时可以省略struct关键字)
1、struct 结构体名 变量名
2、struct 结构体名 变量名={成员1值,成员2值…}
3、定义结构体时顺便创建变量
结构体数组
作用:将自定义的结构体放入到数组中方便维护
语法:struct 结构体名 数组名[元素个数]={{},{},{},…{}};
结构体指针
作用:通过指针访问结构体中的成员
利用操作符 -> 可以通过结构体指针访问结构体属性
结构体嵌套结构体
作用:结构体中的成员可以是另一个结构体
代码示例
#include<iostream>
#include<string>
using namespace std;
struct student
{
string name;
int age;
double score;
};
struct teacher
{
string name;
int id;
int age;
student stu;
};
int main()
{
teacher t;
t.age = 34;
t.id = 34445;
t.name = "王文英";
t.stu.age = 16;//结构体中结构体的定义赋值
t.stu.name = "小红";
t.stu.score = 87;
return 0;
}
结构体做函数参数
作用:将结构体作为参数向函数中传递
两种传递方式:
值传递和地址传递
代码示例:
#include<iostream>
#include<string>
using namespace std;
struct student
{
string name;
int age;
double score;
};
void printStudent(struct student s)//值传递
{
cout << "姓名:" << s.name << " 年龄:" << s.age << " 分数:" << s.score << endl;
}
void printStudent1(struct student* p)//地址传递
{
cout << "姓名:" << p->name << " 年龄:" << p->age << " 分数:" << p->score << endl;
}
int main()
{
student t;
t.age = 34;
t.score = 88;
t.name = "王文英";
printStudent(t);
printStudent1(&t);
return 0;
}
结构体中const使用场景
作用:用const来防止误操作
将函数中的形参改为指针,可以减少内存空间,而且不会复制新的副本
在传地址时在形参前加const可以防止修改数据
结构体案例1
#include<iostream>
#include<string>
#include<ctime>
using namespace std;
struct Student
{
string name;
int score;
};
struct Teacher
{
string tname;
struct Student sArray[5];
};
void allocateSpace(struct Teacher tArray[],int len)
{
string nameSeed = "ABCDE";
for (int i = 0; i < len; i++)
{
tArray[i].tname = "Teacher_";
tArray[i].tname += nameSeed[i];
for (int j = 0; j < 5; j++)
{
tArray[i].sArray[j].name = "Student_";
tArray[i].sArray[j].name += nameSeed[j];
int random = rand() % 61 + 40;
tArray[i].sArray[j].score = random;
}
}
}
void printInfo(struct Teacher tArray[], int len)
{
for (int i = 0; i < len; i++)
{
cout << "老师姓名:" << tArray[i].tname << endl;
for (int j = 0; j < 5; j++)
{
cout << "\t学生姓名:" << tArray[i].sArray[j].name <<
" 考试分数:" << tArray[i].sArray[j].score << endl;
}
}
}
int main()
{
srand((unsigned int)time(NULL));
struct Teacher tArray[3];
int len = sizeof(tArray) / sizeof(tArray[0]);
allocateSpace(tArray, len);
printInfo(tArray, len);
return 0;
}
结构体案例2
#include<iostream>
#include<string>
using namespace std;
struct Hero
{
string name;
int age;
string sex;
};
void bubbleSort(struct Hero hArray[],int len)
{
for (int i = 0; i < len - 1; i++)
{
for (int j = 0; j < len - i - 1; j++)
{
if (hArray[j].age > hArray[j + 1].age)
{
struct Hero temp = hArray[j + 1];
hArray[j + 1] = hArray[j];
hArray[j] = temp;
}
}
}
};
void printHero(struct Hero hArray[],int len)
{
for (int i = 0; i < len; i++)
{
cout << "姓名:" << hArray[i].name
<< " 年龄:" << hArray[i].age
<< " 性别:" << hArray[i].sex << endl;
}
}
int main()
{
struct Hero hArray[5] =
{
{"刘备",23,"男"},
{"关羽",22,"男"},
{"张飞",28,"男"},
{"赵云",21,"男"},
{"貂蝉",19,"女"}
};
int len = sizeof(hArray) / sizeof(hArray[0]);
cout << "排序前打印结果为:" << endl;
for (int i = 0; i < len; i++)
{
cout << "姓名:" << hArray[i].name
<< " 年龄:" << hArray[i].age
<< " 性别:" << hArray[i].sex << endl;
}
bubbleSort(hArray, len);
cout << "排序后打印结果:" << endl;
printHero(hArray, len);
system("pause");
return 0;
}
通讯录管理系统案例
#include<iostream>
#include<string>
#define MAX 1000
using namespace std;
//设计联系人结构体
struct Person
{
//姓名
string m_Name;
//性别 1、男 2、女
int m_Sex;
//年龄
int m_Age;
//电话
string m_Phone;
//住址
string m_Addr;
};
//设计通讯录结构体
struct Addressbooks
{
//通讯录中保存的联系人数组
struct Person personArray[MAX];
//通讯录中当前记录联系人个数
int m_Size;
};
//1、添加联系人
void addPerson(Addressbooks* abs)
{
//判断通讯录是否已满,如果满了就不再添加
if (abs->m_Size == MAX)
{
cout << "通讯录已满,无法添加!" << endl;
return;
}
else
{
//添加具体联系人
//姓名
string name;
cout << "请输入姓名:" << endl;
cin >> name;
abs->personArray[abs->m_Size].m_Name = name;
//性别
cout << "请输入性别: " << endl;
cout << "1----男" << endl;
cout << "2----女" << endl;
int sex = 0;
while (true)
{
cin >> sex;
if (sex == 1 || sex == 2)
{
abs->personArray[abs->m_Size].m_Sex = sex;
break;
}
cout << "输入有误,请重新输入" << endl;
}
//年龄
cout << "请输入年龄: " << endl;
int age = 0;
cin >> age;
abs->personArray[abs->m_Size].m_Age = age;
//电话
cout << "请输入联系电话:" << endl;
string phone;
cin >> phone;
abs->personArray[abs->m_Size].m_Phone = phone;
//住址
cout << "请输入家庭住址:" << endl;
string address;
cin >> address;
abs->personArray[abs->m_Size].m_Addr = address;
//更新通讯录人数
abs->m_Size++;
cout << "添加成功" << endl;
system("pause");//请按任意键继续
system("cls");//清屏操作
}
}
//2、显示所有联系人
void showPerson(Addressbooks *abs)
{
//判断通讯录中人数是否为0,如果为0,提示记录为空
//如果不为0,显示记录的联系人信息
if (abs->m_Size == 0)
{
cout << "当前记录为空" << endl;
}
else
{
for (int i = 0; i < abs->m_Size; i++)
{
cout << "姓名:" << abs->personArray[i].m_Name << "\t";
cout << "性别:" << (abs->personArray[i].m_Sex == 1 ? "男" : "女") << "\t";
cout << "年龄:" << abs->personArray[i].m_Age << "\t";
cout << "电话:" << abs->personArray[i].m_Phone << "\t";
cout << "住址:" << abs->personArray[i].m_Addr << endl;
}
}
system("pause");//按任意键继续
system("cls");//清屏
}
//检测联系人是否存在,如果存在,返回联系人所在数组中的具体位置,不存在返回-1
int isExist(Addressbooks* abs, string name)
{
for (int i = 0; i < abs->m_Size; i++)
{
if (abs->personArray[i].m_Name == name)
{
return i;//找到了,返回这个人在数组中的下标编号
}
}
return -1;//如果遍历结果没有找到,返回-1
}
//3、删除指定联系人
void deletePerson(Addressbooks* abs)
{
cout << "请输入您要删除的联系人姓名:" << endl;
string name;
cin >> name;
//ret == -1 未查到
//ret != -1 查到了
int ret = isExist(abs, name);
if (ret != -1)
{
//查到此人,要进行删除操作
for (int i = ret; i < abs->m_Size; i++)
{
//数据前移
abs->personArray[i] = abs->personArray[i + 1];
}
abs->m_Size--;//更新通讯录中的人员数
cout << "删除成功" << endl;
}
else
{
cout << "查无此人" << endl;
}
system("pause");
system("cls");
}
//4、查找联系人
void findPerson(Addressbooks* abs)
{
cout << "请输入您要查找的联系人:" << endl;
string name;
cin >> name;
//判断指定的联系人是否存在于通讯录中
int ret = isExist(abs, name);
if (ret != -1)//找到联系人
{
cout << "姓名:" << abs->personArray[ret].m_Name << "\t";
cout << "性别:" << abs->personArray[ret].m_Sex << "\t";
cout << "年龄:" << abs->personArray[ret].m_Age << "\t";
cout << "电话:" << abs->personArray[ret].m_Phone << "\t";
cout << "住址:" << abs->personArray[ret].m_Addr << endl;
}
else//未找到联系人
{
cout << "查无此人" << endl;
}
//按任意键后清屏
system("pause");
system("cls");
}
//5、修改指定联系人
void modifyPerson(Addressbooks* abs)
{
cout << "请输入您要修改的联系人" << endl;
string name;
cin >> name;
int ret = isExist(abs, name);
if (ret != -1)//找到指定联系人
{
//姓名
string name;
cout << "请输入姓名:" << endl;
cin >> name;
abs->personArray[ret].m_Name = name;
//性别
cout << "请输入性别:" << endl;
cout << "1----男" << endl;
cout << "2----女" << endl;
int sex = 0;
while (true)
{
cin >> sex;
if (sex == 1 || sex == 2)
{
//输入正确退出循环输入
abs->personArray[ret].m_Sex = sex;
break;
}
cout << "输入错误,请重新输入" << endl;
}
//年龄
cout << "请输入年龄:" << endl;
int age = 0;
cin >> age;
abs->personArray[ret].m_Age = age;
//电话
cout << "请输入联系电话:" << endl;
string phone;
cin >> phone;
abs->personArray[ret].m_Phone = phone;
//地址
cout << "请输入家庭住址:" << endl;
string address;
cin >> address;
abs->personArray[ret].m_Addr = address;
cout << "修改成功!" << endl;
}
else//未找到联系人
{
cout << "查无此人" << endl;
}
system("pause");
system("cls");
}
//6、清空所有联系人
void clearPerson(Addressbooks* abs)
{
cout << "是否真的清空:" << endl;
cout << "1---清空" << endl;
cout << "2---不清空" << endl;
int rc = 0;
cin >> rc;
if (rc == 1)
{
abs->m_Size = 0;//将当前记录联系人数重置为0,做逻辑清空操作
cout << "通讯录已清空" << endl;
}
system("pause");
system("cls");
}
//菜单界面
void showMenu()
{
cout << "***************************" << endl;
cout << "***** 1、添加联系人 *****" << endl;
cout << "***** 2、显示联系人 *****" << endl;
cout << "***** 3、删除联系人 *****" << endl;
cout << "***** 4、查找联系人 *****" << endl;
cout << "***** 5、修改联系人 *****" << endl;
cout << "***** 6、清空联系人 *****" << endl;
cout << "***** 0、退出通讯录 *****" << endl;
cout << "***************************" << endl;
}
int main()
{
//创建通讯录结构体变量
Addressbooks abs;
//初始化通讯录中当前人员个数
abs.m_Size = 0;
int select = 0;//创建用户选择输入的变量
while (true)
{
//菜单调用
showMenu();
cin >> select;
switch (select)
{
case 1://1、添加联系人
addPerson(&abs);
break;
case 2://2、显示联系人
showPerson(&abs);
break;
case 3://3、删除联系人
deletePerson(&abs);
break;
case 4://4、查找联系人
findPerson(&abs);
break;
case 5://5、修改联系人
modifyPerson(&abs);
break;
case 6://6、清空联系人
clearPerson(&abs);
break;
case 0://0、退出通讯录
cout << "欢迎下次使用" << endl;
system("pause");
return 0;
break;
default:
break;
}
}
system("pause");
return 0;
}
c++面向对象编程技术
内存分区模型
c++在执行时,将内存划分为4个区域
运行前:
1、代码区:存放函数体的二进制代码,由操作系统进行管理的
2、全局区:存放全局变量和静态变量以及常量
运行后:
3、栈区:由编译器自动分配释放,存放函数的参数的值,局部变量等
4、堆区:由程序员分配和释放,若程序员不释放,程序结束时由操作系统回收
意义:
不同区域存放的区域,赋予不同的生命周期,给我们更大的灵活编程
代码区
全局区
全局变量,静态变量,常量 由系统掌管是否存在
静态变量:在普通变量前面加static,属于静态变量
常量:字符串常量和const修饰的变量
const修饰的变量:const修饰的全局变量和const修饰的局部变量
不在全局区中:局部变量、const修饰的局部变量(局部常量)
在全局区中:全局变量、静态变量、常量(字符串常量,const修饰的全局变量(全局常量))
栈区
注意:1、不要返回局部变量的地址
2、栈区开辟的数据由编译器管理开辟和释放
堆区
在c++中主要利用new在堆区开辟内存
堆区的数据,由程序员管理开辟,管理员管理释放
如果想释放堆区的数据,利用关键字delete
new
利用new创建的数据,会返回数据对应的类型的指针
语法: new 数据类型
int *p = new int(10);
delete p;
在堆中利用new开辟数组
释放数组的时候需要加[ ]才可以
int *arr = new int [10];//10代表数组有10个元素,返回数组的首地址
//释放数组的时候需要加[ ]才可以
delete[] arr;
引用
作用:给变量起别名
语法: 数据类型 &别名=原名;
注意事项:1、引用必须初始化
2、引用在初始化后,不可以改变
引用做函数参数
作用:函数传参时,可以利用引用的技术让形参修饰实参
优点:可以简化指针修改实参
引用做函数返回值
作用:引用时可以作为函数返回值存在的
注意:不要返回局部变量的引用
用法:函数调用作为左值
#include<iostream>
using namespace std;
int& test01()
{
static int a = 8;
return a;
}
int main()
{
int& ref1 = test01();
cout << "ref1=" << ref1 << endl;
test01() = 1000;//如果函数的返回值是引用,这个函数调用可以作为左值
cout << "ref1=" << ref1 << endl;
system("pause");
return 0;
}
引用的本质
本质:引用的本质在c++内部实现是一个指针常量
常量引用
作用:常量引用主要用来修饰形参,防止误操作
在函数形参列表中可以加const修饰形参,防止形参改变实参
int a=10;
int &ref = 10;
//int& ref = 10;//wrong
const int& ref = 10;
//加上const之后,编译器将代码修改 int temp =10;const int &ref = temp;
函数的默认参数
语法:返回值类型 函数名(参数=默认值){ }
如果我们自己传入数据,就用自己的数据,如果没有,那么就用默认值
注意事项:
1、如果某个位置已经有了默认参数,那么从这个位置往后,从左到右都必须有默认值
2、如果函数声明有默认参数,函数实现就不能有默认参数(声明和实现只能有一个有默认参数)
函数的占位参数
语法:返回值类型 函数名(数据类型){ }
占位参数还可以有默认参数
#include<iostream>
using namespace std;
void func(int a,int=10)
{
cout << "ada" << endl;
}
int main()
{
func(10);//有默认参数可以只传一个
func(10,78);
system("pause");
return 0;
}
函数重载
作用:函数名相同,提高复用性
满足条件
1、同一个作用域下
2、函数名称相同
3、函数参数类型不同,或者个数不同,或者顺序不同
注意:函数的返回值不可以作为函数重载的条件
函数重载示例
#include<iostream>
using namespace std;
void func()
{
cout << "func的调用" << endl;
}
void func(int a)
{
cout << "func(int a)的调用" << endl;
}
void func(double a)
{
cout << "func(double a)的调用" << endl;
}
void func(int a, double b)
{
cout << "func(int a,double b)的调用" << endl;
}
void func(double a, int b)
{
cout << "func(double a,int b)的调用" << endl;
}
int main()
{
func();
func(6);
func(5, 6.4);
func(5.7, 8);
system("pause");
return 0;
}
函数重载的注意事项
1、引用作为函数重载条件
2、函数重载遇到函数默认参数
类和对象
c++面向对象三大特征:封装、继承、多态
实例化
通过一个类,创建一个对象的过程
封装
专业术语
类中的属性和行为 我们统称为 成员
属性-----成员属性、成员变量
行为-----成员函数、成员方法
意义:
1、将属性和行为作为一个整体,表现生活中的事物
2、将属性和行为加以权限控制
语法:
class 类名{ 访问权限:属性/行为 };
访问权限
1、public 公共权限
2、protected 保护权限
3、private 私有权限
public 公共权限
成员 类内可以访问 类外可以访问
protected 保护权限
成员 类内可以访问 类外不可以访问
儿子可以访问父亲中的保护内容
private 私有权限
成员 类内可以访问 类外不可以访问
儿子不可以访问父亲的私有内容
struct和class区别
struct默认权限为公共
class 默认权限为私有
成员属性设置为私有
优点:
1、将所有成员属性设置为私有,可以自己控制读写权限
2、对于写权限,我们可以检测数据的有效性
类案例
#include<iostream>
#include<string>
using namespace std;
class Cube
{
public:
void setL(int l)
{
m_l = l;
}
void setH(int h)
{
m_h = h;
}
void setW(int w)
{
m_w = w;
}
int getL()
{
return m_l;
}
int getH()
{
return m_h;
}
int getW()
{
return m_w;
}
int calculateS()
{
return (m_l * m_h + m_l * m_w + m_h * m_w) * 2;
}
int calculateV()
{
return m_l * m_w * m_h;
}
bool isSameByClass(Cube& c3)
{
if (m_l == c3.getL() && m_w == c3.getW() && m_h == c3.getH())
{
return true;
}
else
return false;
}
private:
int m_l;
int m_h;
int m_w;
};
bool isSame(Cube& c1, Cube& c2)
{
if (c1.getL() == c2.getL() && c1.getW() == c2.getW() && c1.getH() == c2.getH())
{
return true;
}
else
return false;
}
int main()
{
Cube c1;
c1.setL(10);
c1.setW(8);
c1.setH(9);
cout << "c1的面积是:" << c1.calculateS() << endl;
cout << "c1的体积是:" << c1.calculateV() << endl;
Cube c2;
c2.setL(10);
c2.setW(8);
c2.setH(9);
bool ret = isSame(c1, c2);
if (ret)
{
cout << "c1和c2相等的!" << endl;
}
else
{
cout << "c1和c2不相等!" << endl;
}
ret = c1.isSameByClass(c2);
if (ret)
{
cout << "c1和c2相等的!" << endl;
}
else
{
cout << "c1和c2不相等!" << endl;
}
system("pause");
return 0;
}
点是否在圆内案例----可以在多个.cpp 和 .h 中实现不同的类
circle.h
#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;
};
circle.cpp
#include"circle.h"
void Circle::setR(int r)
{
m_r = r;
}
int Circle::getR()
{
return m_r;
}
void Circle::setCenter(Point center)
{
m_center = center;
}
Point Circle::getCenter()
{
return m_center;
}
point.h
#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;
};
point .cpp
#include"point.h"
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;
}
main.cpp
#include"circle.h"
#include"point.h"
//判断点和圆的关系
void isInCircle(Circle& c, Point& p)
{
int distance =//计算两点之间的距离
(c.getCenter().getX() - p.getX()) * (c.getCenter().getX() - p.getX()) +
(c.getCenter().getY() - p.getY()) * (c.getCenter().getY() - p.getY());
//计算半径的平方
int rdistance = c.getR() * c.getR();
//判断关系
if (distance == rdistance)
{
cout << "点在圆上" << endl;
}
else if (distance > rdistance)
{
cout << "点在圆外" << endl;
}
else
{
cout << "点在圆内" << endl;
}
}
int main()
{
Circle c1;
Point p1, p2, p3, p4;
c1.setR(10);
p1.setX(10);
p1.setY(0);
c1.setCenter(p1);
p2.setX(10);
p2.setY(10);
p3.setX(10);
p3.setY(9);
p4.setX(10);
p4.setY(11);
isInCircle(c1, p2);
isInCircle(c1, p3);
isInCircle(c1, p4);
system("pause");
return 0;
}
构造函数和析构函数
构造函数和析构函数----对象的初始化和清理
c++利用构造函数和析构函数解决这个问题
对象的初始化和清理工作时编译器强制要我们做的事情,如果我们不提供构造函数和析构函数,编译器辉提供
编译器提供的构造函数和析构函数是空实现
构造函数:主要作用于创建对象时为对象的成员属性赋值,构造函数有编译器自动调用,无须手动调用
析构函数:主要作用在于对象销毁前系统自动调用,执行一些清理工作
构造函数
语法: 类名(){}
1、构造函数 没有返回值也不写void
2、函数名称和类名相同
3、构造函数可以有参数,因此可以发生重载
4、程序在调用对象时会自动调用构造,无须手动调用,而且只会调用一次
析构函数
语法: ~类名(){}
1、析构函数 没有返回值 不写void
2、函数名称和类名相同,在名称前加上符号~
3、析构函数不可以有参数,因此不可以发生重载
4、程序在对象销毁前会自动调用析构,无须手动调用,而且只会调用一次
构造函数和析构函数示例
#include<iostream>
using namespace std;
class Person
{
public:
Person()//构造函数
{
cout << "Person 构造函数调用" << endl;
}
~Person()//析构函数
{
cout << "Person 析构函数调用" << endl;
}
};
void test01()
{
Person p;//在栈上的数据,test01执行完毕后,释放这个对象
}
int main()
{
test01();
cout << endl;
Person p;
system("pause");
return 0;
}
构造函数的分类和调用
两种分类方式:
按参数分:有参构造和无参构造
按类型分:普通构造和拷贝构造
三种调用方式:
括号法
显示法
隐式转换法
注意事项1:调用默认构造函数时,不要加(),加括号编译器会认为是一个函数声明,不会认为在创建对象
注意事项2:不要利用拷贝构造函数 初始化匿名对象
构造函数调用示例
#include<iostream>
using namespace std;
class Person
{
public:
Person()//构造函数
{
cout << "Person 默认构造函数调用" << endl;
}
Person(int a)//构造函数
{
m_age = a;
cout << "Person 有参构造函数调用" << endl;
}
//拷贝构造函数
Person(const Person &p)
{
//将传入的人身上的所有属性,拷贝到我身上
m_age = p.m_age;
cout << "Person 拷贝构造函数调用" << endl;
}
~Person()//析构函数
{
cout << "Person 析构函数调用" << endl;
}
int m_age;
};
void test01()
{
Person p;
}
int main()
{
//括号法
Person p1;//默认构造函数调用
Person p2(10);//有参构造函数
Person p3(p2);
//注意事项1:调用默认构造函数时,不要加(),加括号编译器会认为是一个函数声明,不会认为在创建对象
//cout << "p2的年龄:" << p2.m_age << endl;
//cout << "p3的年龄:" << p3.m_age << endl;
//显示法
Person p4;
Person p5 = Person(10);//有参构造
Person p6 = Person(p5);//拷贝构造
//注意事项2:不要利用拷贝构造函数 初始化匿名对象 编译器会认为Person(p3)==Person p3;对象声明
//Person(p3);
//Person(10);//匿名对象 特点:当前行执行结束 后,系统会立即回收掉匿名对象
//隐式转换法
Person p7 = 10;//相当于 写了 Person p7=Person(10);有参构造
Person p8 = p7;//拷贝构造
system("pause");
return 0;
}
拷贝构造函数调用时机
c++中拷贝构造函数调用时机通常有三种情况:
1、使用一个已经创建完毕的对象来初始化一个新对象
2、值传递方式给函数参数传参
3、以值传递返回局部对象
拷贝构造函数三个调用时机示例
#include<iostream>
using namespace std;
class Person
{
public:
Person()
{
cout << "Person默认构造函数调用" << endl;
}
Person(int age)
{
m_age = age;
cout << "Person有参构造函数调用" << endl;
}
Person(const Person& p)
{
m_age = p.m_age;
cout << "Person拷贝构造函数调用" << endl;
}
~Person()
{
cout << "Person析构函数调用" << endl;
}
int m_age;
};
void test01()
{
//1、使用一个已经创建完毕的对象来初始化一个新对象
Person p1(20);
Person p2(p1);
cout << "p2的年龄为:" << p2.m_age << endl;
}
//2、值传递方式给函数参数传参
void doWork(Person p)
{
}
void test02()
{
Person p;
doWork(p);
}
//3、以值传递返回局部对象
Person doWork2()
{
Person p1;
return p1;
}
void test03()
{
Person p = doWork2();
}
int main()
{
test01();
test02();
test03();
system("pause");
return 0;
}
构造函数调用规则
默认情况下,创建一个类,c++编译器至少给一个类添加三个函数:
1、默认构造函数(无参,函数体为空)
2、默认析构函数(无参,函数体为空)
3、默认拷贝构造函数,对属性进行值拷贝
规则:
1、如果用户定义有参构造函数,c++不提供无参构造函数,但是会提供默认拷贝构造函数
2、如果用户定义拷贝构造函数,c++不会提供其它构造函数
深拷贝和浅拷贝
浅拷贝:简单的赋值拷贝操作
深拷贝:在堆区重新申请空间,进行拷贝操作
浅拷贝带来的问题是堆区的内存被重复释放,
浅拷贝的问题要用深拷贝来解决
用深拷贝处理浅拷贝问题示例
#include<iostream>
using namespace std;
class Person
{
public:
Person()
{
cout << "Person默认构造函数调用" << endl;
}
Person(int age,int height)
{
m_age = age;
m_height = new int(height);
cout << "Person有参构造函数调用" << endl;
}
Person(const Person& p)
{
cout << "Person 拷贝构造函数调用" << endl;
m_age = p.m_age;
//深拷贝操作
m_height = new int(*p.m_height);
}
~Person()
{
//析构代码,将堆区开辟的数据做释放操作
if (m_height != NULL)
{
delete m_height;
m_height = NULL;
}
cout << "Person析构函数调用" << endl;
}
int m_age;
int* m_height;
};
void test01()
{
Person p1(18,100);
cout << "p1的年龄是:" << p1.m_age << " 身高为:" << *p1.m_height << endl;
Person p2(p1);
cout << "p2的年龄是:" << p2.m_age << " 身高为:" << *p2.m_height << endl;
}
int main()
{
test01();
system("pause");
return 0;
}
初始化列表
作用:c++提供了初始化列表语法,用来初始化属性
语法:构造函数():属性1(值1),属性2(值2)…{ }
初始化列表示例
#include<iostream>
using namespace std;
class Person
{
public:
Person(int a,int b,int c):m_a(a),m_b(b),m_c(c)//初始化列表初始化属性
{
}
int m_a;
int m_b;
int m_c;
};
void Test01()
{
Person p(30, 20, 10);
cout << "m_a= " << p.m_a << endl;
cout << "m_b= " << p.m_b << endl;
cout << "m_c= " << p.m_c << endl;
}
int main()
{
Test01();
system("pause");
return 0;
}
类对象作为类成员
c++类中的成员可以是另一个类的对象,我们称为该成员为 对象成员
构造的顺序:当其它类对象作为本类成员,构造时候先构造类对象,再构造自身
析构的顺序:与构造顺序相反
代码示例
#include<iostream>
using namespace std;
#include<string>
class Phone
{
public:
Phone(string pname)
{
cout << "Phone的构造函数的调用" << endl;
m_pname = pname;
}
~Phone()
{
cout << "Phone的析构函数被调用" << endl;
}
string m_pname;
};
class Person
{
public:
Person(string name, string pname) :m_name(name),m_phone(pname)
{
cout << "Person的构造函数的调用" << endl;
}
~Person()
{
cout << "Person的析构函数被调用" << endl;
}
//姓名
string m_name;
//手机
Phone m_phone;
};
void test01()
{
Person p("张三", "苹果MAX");
cout << p.m_name << "拿着: " << p.m_phone.m_pname << endl;
}
int main()
{
test01();
system("pause");
return 0;
}
静态成员
静态成员就是成员变量和成员函数前面加上关键字static,称为静态成员
静态成员分为:
静态成员变量
1、所有对象共享同一份数据
2、在编译阶段分配内存
3、类内声明,类外初始化
静态成员函数
1、所有对象共享一个函数
2、静态成员函数只能访问静态成员变量,不可以访问非静态成员变量
静态成员函数有两种访问方式:
1、通过对象
2、通过类名作用域限定符
静态成员函数也是有访问权限的,类外访问不到私有静态成员函数
静态成员函数示例
#include<iostream>
using namespace std;
class Person
{
public:
static void func()
{
m_a = 8;
//m_c = 9;//静态成员函数无法访问非静态成员变量
cout << "static void func()被调用" << endl;
}
static int m_a;
int m_c;
};
int Person::m_a = 6;
void test01()
{
Person p;
p.func();//通过对象访问
}
void test02()
{
Person::func();//通过类名作用域限定符访问
}
int main()
{
test01();
test02();
system("pause");
return 0;
}
c++对象模型和this指针
在c++中,类内的成员变量和成员函数分开存储
只有非静态成员变量才属于类的对象
空对象占用内存空间为:1
c++编译器会给每个空对象也分配一个字节空间,是为了区分空对象占内存的位置
每个空对象也应该有一个独一无二的内存地址
this指针
本质上是指针常量 指针的指向是不可以修改的
☆ this指针指向被调用的成员函数所属的对象
this指针是隐含在每一个非静态成员函数内的一种指针
this指针不需要定义,直接使用即可
this指针的用途:
1、当形参和成员变量同名时,可以用this指针来区分
2、在类的非静态成员函数中返回对象本身,可使用return *this
this指针用途代码示例
#include<iostream>
using namespace std;
class Person
{
public:
Person(int age)
{
this->age = age;//形参和成员变量同名时,用this指针来区分
}
Person& PersonAddPerson(Person &p)//要返回引用,返回值是拷贝构造新的对象
{
this->age += p.age;
return *this;//返回对象本身
}
int age;
};
void test01()
{
Person p1(23);
cout << "p1的年龄是:" << p1.age << endl;
}
void test02()
{
Person p2(11);
Person p3(11);
p3.PersonAddPerson(p2).PersonAddPerson(p2).PersonAddPerson(p2);
cout << "p3的年龄是:" << p3.age << endl;
}
int main()
{
test01();
test02();
system("pause");
return 0;
}
## 空指针访问成员函数
c++中空指针也可以调用成员函数,但是要注意有没有用到this指针
如果用到this指针,需要加以判断保证代码的健壮性
const修饰成员函数
常函数
1、成员函数后加const后我们称这个函数为常函数
2、常函数内不可以修改成员属性
3、成员属性声明时加关键字mutable后,在常函数中依然可以修改
在成员函数后面加const,修饰的是this指针,让指针指向的值也不可以修改
常对象
1、声明对象前加const称该对象为常对象
2、常对象只能调用常函数
常对象常函数代码示例
#include<iostream>
using namespace std;
class Person
{
public:
void showPerson()const//常函数
{
//this->m_a = 100;//不可以修改
//this = NULL;//不可以修改this指针的指向
this->m_b = 100;//加mutable关键字后可以修改
cout << "调用showPerson函数" << endl;
}
void showP2()//普通成员函数
{
cout << "调用showP2函数" << endl;
}
int m_a;
mutable int m_b;
};
void test01()
{
Person p;
p.showPerson();
p.showP2();
}
void test02()
{
const Person p1;
p1.showPerson();
//p1.showP2();//不可以,常对象只能调用常函数
}
int main()
{
test01();
test02();
system("pause");
return 0;
}
友元
友元的目的是让一个函数或者类访问另一个类中私有成员
友元的关键字:friend
友元的三种实现:
1、全局函数做友元
2、类做友元
3、成员函数做友元
全局函数做友元
#include<iostream>
using namespace std;
#include<string>
class Building
{
friend void Visit(Building& b);//加上友元声明后全局函数可以访问类中的私有成员
public:
Building()
{
m_bedrom = "卧室";
m_sittingroom = "客厅";
}
public:
string m_sittingroom;
private:
string m_bedrom;
};
void Visit(Building &b)
{
cout << "正在访问 " << b.m_sittingroom << endl;
cout << "正在访问 " << b.m_bedrom << endl;
}
void test01()
{
Building b;
Visit(b);
}
int main()
{
test01();
system("pause");
return 0;
}
类做友元
成员函数做友元
#include<iostream>//让成员函数visit()可以访问私有对象,而visit2()不可以访问私有对象
using namespace std;
#include<string>
class Building;
class GoodGuy
{
public:
GoodGuy() {};
void visit(Building& b);
void visit2(Building& b);
};
class Building
{
friend void GoodGuy::visit(Building& b);
public:
Building();
public:
string m_sittingroom;
private:
string m_bedroom;
};
void GoodGuy::visit(Building &b)
{
cout << "visit()访问:" << b.m_sittingroom << endl;
cout << "visit()访问:" << b.m_bedroom << endl;
}
void GoodGuy::visit2(Building& b)
{
cout << "visit2()访问:" << b.m_sittingroom << endl;
//cout << "visit2()访问:" << b.m_bedroom << endl;
}
Building::Building()
{
m_sittingroom = "客厅";
m_bedroom = "卧室";
}
void test01()
{
GoodGuy gg;
Building b;
gg.visit(b);
gg.visit2(b);
}
int main()
{
test01();
system("pause");
return 0;
}
运算符重载
对已有的运算符进行重新定义,赋予其另一种功能,以适应不同的数据类型
加号运算符重载
作用:实现两个自定义数据类型相加运算
加号运算符重载代码示例:
#include<iostream>
using namespace std;
class Person
{
public:
//Person operator+(Person& p)//成员函数重载加号运算符
//{
// Person temp;
// temp.m_a = this->m_a + p.m_a;
// temp.m_b = this->m_b + p.m_b;
// return temp;
//}
int m_a;
int m_b;
};
Person operator+(Person& p1, Person& p2)//全局函数重载加号运算符
{
Person temp;
temp.m_a = p1.m_a + p2.m_a;
temp.m_b = p1.m_b + p2.m_b;
return temp;
}
Person operator+(Person& p1, int num)
{
Person temp;
temp.m_a = p1.m_a + num;
temp.m_b = p1.m_b + num;
return temp;
}
void test01()
{
Person p1;
p1.m_a = 10;
p1.m_b = 6;
Person p2;
p2.m_a = 9;
p2.m_b = 5;
Person p3;
//成员函数重载本质调用:
//p3 = p1.operator+(p2);
//全局函数重载运算符本质调用:
//p3 = operator+(p1, p2);
p3 = p1 + p2;//最终简写成这样
//运算符重载也可以发生函数重载
Person p4;
p4 = p1 + 100;
cout << "p3.m_a=" << p3.m_a << endl;
cout << "p3.m_b=" << p3.m_b << endl;
cout << "p4.m_a=" << p4.m_a << endl;
cout << "p4.m_b=" << p4.m_b << endl;
}
int main()
{
test01();
system("pause");
return 0;
}
左移运算符重载
作用:可以输出自定义数据类型
不会利用成员函数重载<<运算符,因为无法实现cout在左侧
只能利用全局函数重载左移运算符
左移运算符重载代码示例
#include<iostream>
using namespace std;
class Person
{
friend ostream& operator<<(ostream& cout, Person& p);
public:
Person(int a,int b)
{
m_a = a;
m_b = b;
}
private:
int m_a;
int m_b;
};
ostream& operator<<(ostream& cout, Person& p)//全局函数重载左移运算符,本质 operator<<(cout,p),简写:cout<< p
{
cout << "p.m_a=" << p.m_a << "\tp.m_b=" << p.m_b;
return cout;
}
void test01()
{
Person p(12, 9);
cout << p << endl;
}
int main()
{
test01();
system("pause");
return 0;
}
递增运算符重载
作用:通过重载递增运算符实现自己的整型数据
递增运算符重载代码示例
#include<iostream>
using namespace std;
class Integer
{
friend ostream& operator<<(ostream& cout,const Integer& num);
public:
Integer()
{
m_num = 0;
}
//前置++运算符重载
Integer& operator++()//返回引用是为了一直对一个数据进行递增操作
{
m_num++;
return *this;
}
//后置++运算符重载
Integer operator++(int)//int代表占位参数,可以用来区别前置和后置递增运算符重载
{//不能返回引用,因为temp是局部变量
Integer temp = *this;
m_num++;
return temp;
}
private:
int m_num;
};
ostream& operator<<(ostream& cout, const Integer& I)//重载<<运算符
{
cout << I.m_num;
return cout;
}
void test01()
{
Integer num;
cout << ++(++num) << endl;
cout << num << endl;
}
void test02()
{
Integer num;
cout << num++ << endl;
cout << num << endl;
}
int main()
{
test01();
test02();
system("pause");
return 0;
}
赋值运算符重载
c++编译器至少给一个类添加4个函数
1、默认构造函数(无参,函数体为空)
2、默认析构函数(无参,函数体为空)
3、默认拷贝构造函数,对属性进行值拷贝
4、赋值运算符operator=,对属性进行值拷贝
如果类中属性指向堆区,做赋值操作时也会出现深浅拷贝问题
赋值运算符重载代码示例:
#include<iostream>
using namespace std;
class Person
{
public:
Person(int age)
{
m_age = new int(age);
}
Person& operator=(Person& p)
{
if (m_age != NULL)
{
delete m_age;
m_age = NULL;
}
m_age = new int(*p.m_age);
return *this;
}
~Person()
{
if (m_age != NULL)
{
delete m_age;
m_age = NULL;
}
}
int* m_age;
};
void test01()
{
Person p1(20);
Person p2(10);
Person p3(60);
p2 = p1 = p3;
cout << "p2=" << *p2.m_age << endl;
cout << "p1=" << *p1.m_age << endl;
cout << "p3=" << *p3.m_age << endl;
}
int main()
{
test01();
system("pause");
return 0;
}
关系运算符重载
作用:重载关系运算符,可以让两个自定义类型对象进行对比操作
关系运算符重载代码示例
#include<iostream>
using namespace std;
#include<string>
class Person
{
public:
Person(string name, int age)
{
m_name = name;
m_age = age;
}
bool operator==(Person& p)
{
if (this->m_age == p.m_age && this->m_name == p.m_name)
{
return true;
}
else
return false;
}
bool operator!=(Person& p)
{
if (this->m_age == p.m_age && this->m_name == p.m_name)
{
return false;
}
else
return true;
}
string m_name;
int m_age;
};
void test01()
{
Person p1("Tom", 23);
Person p2("Tom", 23);
if (p1 == p2)
{
cout << "p1和p2是相等的" << endl;
}
else
{
cout << "p1和p2是不相等的" << endl;
}
if (p1 != p2)
{
cout << "p1和p2是不相等的" << endl;
}
else
{
cout << "p1和p2是相等的" << endl;
}
}
int main()
{
test01();
return 0;
}
函数调用运算符重载
函数调用运算符()也可以重载
由于重载后使用的方式非常向函数的调用,因此称为仿函数
仿函数没有固定写法,非常灵活
函数调用运算符重载代码示例
#include<iostream>
using namespace std;
#include<string>
class MyPrint
{
public:
void operator()(string test)
{
cout << test << endl;
}
int operator()(int num1,int num2)//重载的()运算符也称为仿函数
{
return num1 + num2;
}
};
void test01()
{
MyPrint mp;
mp("helloworld");
}
void test02()
{
MyPrint mp;
int result = mp(34, 67);
cout << result << endl;
cout << MyPrint()(23, 34) << endl;//匿名对象调用
}
int main()
{
test01();
test02();
return 0;
}
继承
子类也称为 派生类
父类也称为基类
继承的基本语法
class 子类:继承方式 父类
继承的好处:减少重复代码
继承方式
公有继承:
父类中的公有权限成员,到子类中依然是公共权限
父类中的保护权限成员,到子类中依然是保护权限
父类中的私有权限成员,子类访问不到
保护继承:
父类中的公有权限成员,到子类中是保护权限
父类中的保护权限成员,到子类中依然是保护权限
父类中的私有权限成员,子类访问不到
私有继承:
父类中的公有,保护权限成员,到子类中是私有继承
父类中的私有权限成员,子类无法访问
继承中的对象模型
父类中所有非静态成员属性都会被子类继承下去
父类中私有成员属性 是被编译器隐藏了,因此访问不到,但是确实继承了
代码示例
#include<iostream>
using namespace std;
class Base
{
public:
int m_a;
protected:
int m_b;
private:
int m_c;
};
class Son :public Base
{
public:
int m_d;
};
void test01()
{
Son s1;
cout << sizeof(Son) << endl;//16
}
int main()
{
test01();
return 0;
}
利用开发人员命令提示工具查看对象模型
跳转盘符 F:
跳转文件路径 cd 具体路径下
查看命名
cl /d1 resportSingleClassLayout类名 文件名
上示代码结果:
继承中构造和析构顺序
先构造父类,再构造子类,析构的顺序和构造的顺序相反
继承同名成员函数处理方式
访问子类同名成员,直接访问即可
访问父类同名成员,需要加作用域
Son s;
s.m_a;
s.Base::m_a;//加一个父类的作用域来访问父类
注意:如果子类中出现和父类同名的成员函数,子类的
同名成员函数会隐藏掉父类中所有同名成员函数
如果想访问父类中被隐藏的成员函数,需要加作用域
继承同名静态成员处理方式
静态成员和非静态成员出现同名,处理方式一致
访问子类同名成员,直接访问即可
访问父类同名成员,需要加作用域
两种访问方式:通过对象和通过类名
//通过类名访问
Son::func();
Son::Base::func();
多继承语法
语法:
class 子类:继承方式 父类1,继承方式 父类2…
多继承可能会引发父类中有同名成员出现,需要加作用域区分
c++开发中不建议使用
当父类中出现同名成员,子类使用需要加作用域加以区分
菱形继承
两个派生类继承同一个基类
又有某个类同时继承两个派生类
这种继承称为菱形继承或者砖石继承
利用虚继承 解决菱形继承的问题
在继承之前加上关键字 virtual 变成虚继承
被虚继承的那个类称为 虚基类
vbptr(virtual base pointer)虚基类指针 指向:
vbtable:虚基类表
是继承一个指针,通过偏移量来找到同一个属性
虚继承代码示例
#include<iostream>
using namespace std;
class Base
{
public:
int m_a;
};
class Sheep :virtual public Base{};
class Tuo :virtual public Base {};
class SheepTuo :public Sheep, public Tuo{};
void test01()
{
SheepTuo st1;
//st1.Sheep::m_a = 100;
//st1.Tuo::m_a = 200;
st1.m_a = 90;
cout << "st1.Tuo=" << st1.Tuo::m_a << endl;
cout << "at1.Sheep=" << st1.Sheep::m_a << endl;
}
int main()
{
test01();
return 0;
}
多态
分为两类:
1、静态多态:函数重载和运算符重属于静态多态,复用函数名
2、动态多态:派生类和虚函数实现运行时多态
静态多态和动态多态区别:
1、静态多态的函数地址早绑定,编译阶段确定函数地址
2、动态多态的函数地址晚绑定,运行阶段确定函数地址
动态多态满足条件:
1、有继承关系
2、子类重写父类虚函数
重写:函数返回值 函数名 参数列表 完全相同
在子类中重写时virtual可写可不写,在父类中一定要写
动态多态使用:
父类的指针或者引用 指向子类对象
代码示例
#include<iostream>
using namespace std;
class Animal
{
public:
virtual void speak()
{
cout << "animal在说话" << endl;
}
};
class Dog :public Animal
{
void speak()
{
cout << "dog在说话" << endl;
}
};
void DoSpeak(Animal& animal)
{
animal.speak();
}
void test01()
{
Dog dog;
DoSpeak(dog);
}
int main()
{
test01();
return 0;
}
vfptr:(virtual function point)虚函数(表)指针
指向 虚函数表vftable:表内记录虚函数地址
当子类重写父类虚函数
子类中的虚函数表内部会替换成 子类中的虚函数地址
当父类的指针或者引用指向子类对象时,发生多态
多态的优点:
1、代码组织结果清晰
2、可读性强
3、利于前期和后期的扩展以及维护
开闭原则:对扩展进行开发,对修改进行关闭
用多态实现计算机案例
#include<iostream>
using namespace std;
class CalculateBase
{
public:
virtual int getNum()
{
return 0;
}
int m_num1;
int m_num2;
};
class AddCalculate:public CalculateBase
{
public:
int getNum()
{
return m_num1 + m_num2;
}
};
class SubCalculate :public CalculateBase
{
public:
int getNum()
{
return m_num1 - m_num2;
}
};
class MulCalculate :public CalculateBase
{
public:
int getNum()
{
return m_num1 * m_num2;
}
};
void test01()
{
//父类的指针指向子类对象
CalculateBase* p = new AddCalculate;
p->m_num1 = 100;
p->m_num2 = 30;
cout << p->m_num1 << "+" << p->m_num2 << "=" << p->getNum() << endl;
delete p;
p = new SubCalculate;
p->m_num1 = 100;
p->m_num2 = 30;
cout << p->m_num1 << "-" << p->m_num2 << "=" << p->getNum() << endl;
delete p;
p = new MulCalculate;
p->m_num1 = 100;
p->m_num2 = 30;
cout << p->m_num1 << "*" << p->m_num2 << "=" << p->getNum() << endl;
delete p;
//父类的引用指向子类对象
AddCalculate uu;
CalculateBase& u = uu;
u.m_num1 = 10;
u.m_num2 = 40;
cout << u.m_num1 << "+" << u.m_num2 << "=" << u.getNum() << endl;
}
int main()
{
test01();
return 0;
}
纯虚函数和抽象类
纯虚函数语法:
virtual 返回值类型 函数名{参数列表}=0;
当类中有纯虚函数,这个类也称为抽象类
抽象类特点:
1、无法实例化对象
2、子类必须重写抽象类中的纯虚函数,否则也属于抽象类
纯虚函数代码示例
#include<iostream>
using namespace std;
class Base
{
public:
virtual void func() = 0;
};
class Son :public Base
{
public:
void func()
{
cout << "调用Son中func()" << endl;
}
};
void test01()
{
Base* b = new Son;
b->func();
}
int main()
{
test01();
return 0;
}
用纯虚函数和抽象类解决制作饮品代码案例
#include<iostream>
using namespace std;
class AbstructDrinking
{
public:
virtual void Boil() = 0;//煮水
virtual void PourInCup() = 0;//倒入杯中
virtual void PutSomething() = 0;//加入佐料
void makeDrink()
{
Boil();
PourInCup();
PutSomething();
}
};
class Tea:public AbstructDrinking
{
public:
void Boil()
{
cout << "倒入矿泉水" << endl;
}
void PourInCup()
{
cout << "倒入水杯" << endl;
}
void PutSomething()
{
cout << "加入茶叶" << endl;
}
};
class Coffee:public AbstructDrinking
{
public:
void Boil()
{
cout << "倒入热水" << endl;
}
void PourInCup()
{
cout << "倒入咖啡杯" << endl;
}
void PutSomething()
{
cout << "加入糖" << endl;
}
};
void doWork(AbstructDrinking* abs)
{
abs->makeDrink();
delete abs;
}
void test01()
{
doWork(new Tea);
cout << "----------------------------" << endl;
doWork(new Coffee);
}
int main()
{
test01();
return 0;
}
虚析构和纯虚析构
多态使用时,如果子类中有属性开辟到堆区,那么父类指针在释放时无法调用到子类的析构代码
解决方式:
将父类中的析构函数改为虚析构或者纯虚析构
虚析构和纯虚析构共性:
1、可以解决父类指针释放子类对象
2、都需要有具体的函数实现
虚析构和纯虚析构区别:
1、如果是纯虚析构,该类属于抽象类,无法实例化对象
虚析构语法:
virtual ~类名(){}
纯虚析构语法:
virtual ~类名()=0;
利用虚析构可以解决 父类指针释放子类对象时不干净的问题
代码示例
#include<iostream>
using namespace std;
#include<string>
class Animal
{
public:
Animal()
{
cout << "调用Animal构造函数" << endl;
}
virtual~Animal()//虚析构函数
{
cout << "调用Animal析构函数" << endl;
}
//virtual ~Animal() = 0;//纯虚析构函数
virtual void show() = 0;
};
//Animal::~Animal()//纯虚析构函数具体实现
//{
// cout << "调用Animal纯虚析构函数" << endl;
//}
class Cat:public Animal
{
public:
Cat(string name)
{
cout << "调用cat构造函数" << endl;
m_name = new string(name);
}
~Cat()
{
if (this->m_name != NULL)
{
delete m_name;
m_name = NULL;
cout << "调用cat析构函数" << endl;
}
}
void show()
{
cout << *m_name << "cat在说话" << endl;
};
string* m_name;
};
void test01()
{
Animal* animal = new Cat("Tom");
animal->show();
delete animal;
}
int main()
{
test01();
return 0;
}
结果:
如果子类中没有堆区数据,可以不写为虚析构或者纯虚析构
多态:电脑组装代码案例
#include<iostream>
using namespace std;
//抽象CPU类
class CPU
{
public:
virtual void calculate() = 0;
};
//抽象显卡类
class VideoCard
{
public:
virtual void display() = 0;
};
//抽象内存条类
class Memory
{
public:
virtual void storage() = 0;
};
class Computer
{
public:
Computer(CPU* cpu, VideoCard* vc, Memory* m)
{
m_cpu = cpu;
m_vc = vc;
m_m = m;
}
void Work()
{
m_cpu->calculate();
m_vc->display();
m_m->storage();
}
~Computer()
{
if (this->m_cpu != NULL)
{
delete m_cpu;
m_cpu = NULL;
}
if (this->m_vc != NULL)
{
delete m_vc;
m_vc = NULL;
}
if (this->m_m != NULL)
{
delete m_m;
m_m = NULL;
}
}
private:
CPU *m_cpu;
VideoCard *m_vc;
Memory *m_m;
};
class InterCPU:public CPU
{
public:
void calculate()
{
cout << "InterCPU开始计算" << endl;
}
};
class InterVideoCard:public VideoCard
{
public:
void display()
{
cout << "InterVideoCard开始显示" << endl;
}
};
class InterMemory:public Memory
{
public:
void storage()
{
cout << "InterMemory开始存储" << endl;
}
};
class VrCPU :public CPU
{
public:
void calculate()
{
cout << "VrCPU开始计算" << endl;
}
};
class VrVideoCard :public VideoCard
{
public:
void display()
{
cout << "VrVideoCard开始显示" << endl;
}
};
class VrMemory :public Memory
{
public:
void storage()
{
cout << "VrMemory开始存储" << endl;
}
};
void test01()
{
//第一台电脑零件
CPU* intercpu = new InterCPU;
VideoCard* intervideocard = new InterVideoCard;
Memory* intermemory = new InterMemory;
//第一台电脑创建
cout << "第一台电脑开始工作:" << endl;
Computer* computer1 = new Computer(intercpu, intervideocard, intermemory);
computer1->Work();
delete computer1;
cout << "-------------------------------------------" << endl;
//第二台电脑创建
cout << "第二台电脑开始工作:" << endl;
Computer* computer2 = new Computer(new VrCPU,new VrVideoCard,new VrMemory);
computer2->Work();
delete computer2;
cout << "-------------------------------------------" << endl;
//第三台电脑创建
cout << "第三台电脑开始工作:" << endl;
Computer* computer3 = new Computer(new VrCPU, new InterVideoCard, new VrMemory);
computer3->Work();
delete computer3;
}
int main()
{
test01();
return 0;
}
文件操作
程序运行时产生的数据属于临时数据,程序一旦运行结束都会被释放
通过文件可以将数据持久化
c++中对文件操作需要包含头文件
文件类型分为两种:
1、文本文件:文件以文本的ASCll码形式存储在计算机中
2、二进制文件:文件以文本的二进制形式存储在计算机中,用户一般不能直接读懂它们
操作文件的三大类:
1、ofstream:写操作
2、ifstream:读操作
3、fstream:读写操作
文本文件
写文件:
步骤:
1、包含头文件
#include
2、创建流对象
ofstream ofs;
3、打开文件
ofs.open(“文件路径”,打开方式);
4、写数据
ofs<<“写入的数据”;
5、关闭文件
ofs.close();
文件打开方式:
写文件代码示例
#include<iostream>
using namespace std;
#include<fstream>
int main()
{
ofstream ofs;
ofs.open("text.txt", ios::out);
ofs << "姓名: 小明" << endl;
ofs << "年龄: 17" << endl;
ofs << "体重: 56kg" << endl;
ofs << "记录日期:2021/9/11" << endl;
ofs.close();
}
读文件
步骤:
1、包含头文件
#include
2、创建流对象
ifstream ifs;
3、打开文件并判断文件是否打开成功
ifs.open(“文件路径”,打开方式);
4、读数据
四种方式读取
5、关闭文件
ifs.close();
读文件代码示例
#include<iostream>
using namespace std;
#include<fstream>
#include<string>
int main()
{
ifstream ifs;
ifs.open("text.txt", ios::in);
if (!ifs.is_open())
{
cout << "文件打开失败!" << endl;
return 0;
}
//读数据
//第一种:
/*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 end of file//不推荐使用这种,效率低
{
cout << c;
}
ifs.close();
}
二进制文件
以二进制的方式对文件进行读写操作
打开方式要指定为ios::binary
写文件
二进制方式写文件主要利用流对象调用成员函数write
函数原型:ostream& white(const char* buffer,int len);
参数解释:字符指针buffer指向内存中一段存储空间,len是读写的字节数
写文件代码示例
#include<iostream>
using namespace std;
#include<fstream>
class Person
{
public:
char m_name[30];
int m_age;
};
int main()
{
ofstream ofs("Person.txt", ios::out | ios::binary);
//ofs.open("Person.txt",ios::out|ios::binary);可以直接接着上面写
Person p = { "张三",34 };
ofs.write((const char*)&p, sizeof(p));
ofs.close();
return 0;
}
读文件
二进制方式读文件主要利用流对象调用成员函数read
函数原型 istream& read(char *buffer,int len);
参数解释:字符指针buffer指向内存中一段存储空间,len是读写的字节数
读文件代码示例
#include<iostream>
using namespace std;
#include<fstream>
class Person
{
public:
char m_name[30];
int m_age;
};
int main()
{
ifstream ifs;
ifs.open("Person.txt",ios::in|ios::binary);
if (!ifs.is_open())
{
cout<<"文件打开失败!"<<endl;
}
Person p;
ifs.read((char*)&p, sizeof(Person));
cout << "姓名:" << p.m_name << "\t年龄:" << p.m_age << endl;
ifs.close();
return 0;
}
职工管理系统代码案例
worker.h
#pragma once
#include<iostream>
using namespace std;
#include<string>
//职工抽象类
class Worker
{
public:
//显示个人信息
virtual void showInfo() = 0;
//获取岗位名称
virtual string getDeptName() = 0;
//职工编号
int m_id;
//职工姓名
string m_name;
//部门编号
int m_deptid;
};
employee.h
#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 string getDeptName();
};
employee.cpp
#include"employee.h"
//构造函数
Employee::Employee(int id, string name, int did)
{
this->m_id = id;
this->m_name = name;
this->m_deptid = did;
}
//显示个人信息
void Employee::showInfo()
{
cout << "职工编号:" << this->m_id
<< "\t职工姓名:" << this->m_name
<< "\t岗位:" << this->getDeptName()
<< "\t岗位职责:完成经理交给的任务" << endl;
}
//获取岗位名称
string Employee::getDeptName()
{
return string("员工");
}
manager.h
#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 string getDeptName();
};
manager.cpp
#include"manager.h"
//构造函数
Manager::Manager(int id, string name, int did)
{
this->m_id = id;
this->m_name = name;
this->m_deptid = did;
}
//显示个人信息
void Manager::showInfo()
{
cout << "职工编号:" << this->m_id
<< "\t职工姓名:" << this->m_name
<< "\t岗位:" << this->getDeptName()
<< "\t岗位职责:完成老板交给的任务,并下发任务给员工" << endl;
}
//获取岗位名称
string Manager::getDeptName()
{
return string("经理");
}
boss.h
#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
#include"boss.h"
//构造函数
Boss::Boss(int id, string name, int did)
{
this->m_id = id;
this->m_name = name;
this->m_deptid = did;
}
//显示个人信息
void Boss::showInfo()
{
cout << "职工编号:" << this->m_id
<< "\t职工姓名:" << this->m_name
<< "\t岗位:" << this->getDeptName()
<< "\t岗位职责:管理公司所有事务" << endl;
}
//获取岗位名称
string Boss::getDeptName()
{
return string("总裁");
}
workermanager.h
#pragma once
#include<iostream>
using namespace std;
#include"worker.h"
#include"employee.h"
#include"boss.h"
#include"manager.h"
#include<fstream>
#define FILENAME "emFile.txt"
class WorkerManager
{
public:
WorkerManager();
//展示菜单
void Show_Menu();
//退出系统
void ExitSystem();
//记录职工人数
int m_EmpNum;
//职工数组指针
Worker** m_EmpArray;
//添加职工
void Add_Emp();
//保存文件
void save();
//判断文件是否为空 标志
bool m_FileIsEmpty;
//统计人数
int get_EmpNum();
//初始化员工
void init_Emp();
//显示职工
void Show_Emp();
//删除职工
void Del_Emp();
//判断职工是否存在 如果存在返回职工所在数组中的位置,不存在返回-1
int IsExist(int id);
//修改职工
void Mod_Emp();
//查找职工
void Find_Emp();
//排序职工
void Sort_Emp();
//清空文件
void Clean_File();
//析构函数
~WorkerManager();
};
workermanager.cpp
#include"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;
ifs.close();
return;
}
//2、文件存在,数据为空
char ch;
ifs >> ch;
if (ifs.eof())
{
//文件为空
//cout << "文件为空!" << endl;
//初始化记录人数
this->m_EmpNum = 0;
//初始化数组指针
this->m_EmpArray = NULL;
//初始化文件是否为空
this->m_FileIsEmpty = true;
ifs.close();
return;
}
//3、文件存在,并且记录数据
int num = this->get_EmpNum();
//cout << "职工人数为:" << num << endl;
this->m_EmpNum = num;
//开辟空间
this->m_EmpArray = new Worker * [this->m_EmpNum];
//将文件中的数据,存放到数组中
this->init_Emp();
//测试代码
/*for (int i = 0; i < this->m_EmpNum; i++)
{
cout << "职工编号:" << this->m_EmpArray[i]->m_id
<< " 姓名:" << this->m_EmpArray[i]->m_name
<< " 部门编号:" << this->m_EmpArray[i]->m_deptid << endl;
}*/
}
void WorkerManager::Show_Menu()//展示菜单
{
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);//退出程序
}
//添加职工
void WorkerManager:: Add_Emp()
{
cout << "请输入添加职工数量:" << endl;
int addNum = 0;//保存用户的输入数量
cin >> addNum;
if (addNum > 0)
{
//添加
//计算添加空间大小
int newSize = this->m_EmpNum + addNum;//新空间人数=原来记录人数+新增人数
//开辟新空间
Worker** newSpace = new Worker * [newSize];
//将原来空间下数据,拷贝到新空间下
if (this->m_EmpArray != NULL)
{
for (int i = 0; i < this->m_EmpNum; i++)
{
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;
cout << "请输入第" << i + 1 << "个新职工姓名:" << endl;
cin >> name;
cout << "请选择该职工岗位:" << endl;
cout << "1、普通职工" << endl;
cout << "2、经理" << endl;
cout << "3、老板" << endl;
cin >> dSelect;
Worker* worker = NULL;
switch (dSelect)
{
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;
default:
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");
}
//保存文件
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_deptid << endl;
}
//关闭文件
ofs.close();
}
//统计人数
int WorkerManager::get_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++;
}
return num;
}
//初始化员工
void WorkerManager:: init_Emp()
{
ifstream ifs;
ifs.open(FILENAME, ios::in);
int id;
string name;
int did;
int index = 0;
while (ifs >> id && ifs >> name && ifs >> did)
{
Worker* worker = NULL;
if (did == 1)//普通职工
{
worker = new Employee(id, name, did);
}
else if (did == 2)//经理
{
worker = new Manager(id, name, did);
}
else//老板
{
worker = new Boss(id, name, did);
}
this->m_EmpArray[index] = worker;
index++;
}
//关闭文件
ifs.close();
}
//显示职工
void WorkerManager::Show_Emp()
{
//判断文件是否为空
if (this->m_FileIsEmpty)
{
cout << "文件不存在或记录为空!" << endl;
}
else
{
for (int i = 0; i < m_EmpNum; i++)
{
//利用多态调用程序接口
this->m_EmpArray[i]->showInfo();
}
}
//按任意键后清屏
system("pause");
system("cls");
}
//删除职工
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)//说明职工存在,并且要删除掉index位置上的职工
{
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;
}
else
{
cout << "删除失败,未找到该职工!" << endl;
}
}
//按任意键清屏
system("pause ");
system("cls");
}
//判断职工是否存在 如果存在返回职工所在数组中的位置,不存在返回-1
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::Mod_Emp()
{
if (this->m_FileIsEmpty)
{
cout << "文件不存在或者记录为空!" << endl;
}
else
{
cout << "请输入要修改职工的编号:" << endl;
int id;
cin >> id;
int ret = this->IsExist(id);
if (ret != -1)
{
//查到编号的职工
delete this->m_EmpArray[ret];
int newId = 0;
string newName = "";
int dSelect = 0;
cout << "查到:" << id << "号职工,请输入新职工号:" << endl;
cin >> newId;
cout << "请输入新姓名:" << endl;
cin >> newName;
cout << "请输入岗位:" << endl;
cout << "1、普通职工" << endl;
cout << "2、经理" << endl;
cout << "3、老板" << endl;
cin >> dSelect;
Worker* worker = NULL;
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[ret] = worker;
cout << "修改成功!" << endl;
//保存到文件中
this->save();
}
else
{
cout << "修改失败,查无此人!" << endl;
}
}
//按任意键清屏
system("pause");
system("cls");
}
//查找职工
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 (this->m_EmpArray[i]->m_name == name)
{
cout << "查找成功,职工编号为:"
<< this->m_EmpArray[i]->m_id
<< "号职工信息如下:" << endl;
flag = true;
//调用显示信息接口
this->m_EmpArray[i]->showInfo();
}
}
if (flag == false)
{
cout << "查找失败,查无此人!" << endl;
}
}
else
{
cout << "输入选项有误!" << endl;
}
}
//按任意键清屏
system("pause");
system("cls");
}
//排序职工
void WorkerManager::Sort_Emp()
{
if (this->m_FileIsEmpty)
{
cout << "文件不存在或记录为空!" << endl;
system("pause");
system("cls");
}
else
{
cout << "请选择排序方式:" << endl;
cout << "1、按职工号进行升序" << endl;
cout << "2、按职工号进行降序" << endl;
int select = 0;
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 (m_EmpArray[minOrMax]->m_id < m_EmpArray[j]->m_id)
{
minOrMax = j;
}
}
}
//判断一开始认定的最小值或最大值是不是计算的最小值或最大值,如果不是,交换数据
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();//展示所有职工
}
}
//清空文件
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++)
{
delete this->m_EmpArray[i];
this->m_EmpArray[i] = NULL;
}
//删除堆区数组指针
delete[] this->m_EmpArray;
this->m_EmpArray = NULL;
this->m_EmpNum = 0;
this->m_FileIsEmpty = true;
}
cout << "清空成功!" << endl;
}
system("pause");
system("cls");
}
WorkerManager::~WorkerManager()
{
if (this->m_EmpArray != NULL)
{
delete[] this->m_EmpArray;
this->m_EmpArray = NULL;
}
}
main.cpp
#include<iostream>
using namespace std;
#include"workermanager.h"
#include"worker.h"
//#include"employee.h"
//#include"boss.h"
//#include"manager.h"
int main()
{
测试代码
//Worker* worker = NULL;
//worker = new Employee(1, "张三", 1);
//worker->showInfo();
//delete worker;
//worker = new Manager(2, "李四", 2);
//worker->showInfo();
//delete worker;
//worker = new Boss(3, "王五", 3);
//worker->showInfo();
//delete worker;
//实例化管理者对象
WorkerManager wm;
int choice = 0;//用来存储用户的选项
while (true)
{
wm.Show_Menu();//调用展示菜单成员函数
cout << "请输入您的选择:" << endl;
cin >> choice;//接受用户的选项
switch (choice)
{
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;
}