c++学习1

快捷键

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;
}


  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

&*Savior

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值