前言
最近跟着b站的c++视频做了一个通讯录管理系统的程序,本文会简单分析这种程序代码的结构、设计思路以及涉及其中的一些c++语言知识和用法。
在此附上相关视频链接:https://www.bilibili.com/video/BV1et411b73Z?p=83&spm_id_from=pageDriver
如若涉及侵权,请告知作者删改
一、程序设计思路
先来看看这个程序的需要的功能
从这张图片我们可以看到,这样的一个通讯录管理系统的功能上就有7个选项,除开最后的“退出通讯录”,共有6个功能。从功能导向的思路来看,我们至少需要6个相应的函数去实现。
除此之外,我们还需要显示该菜单,除了0选项之外,每个功能使用后还可再次使用,或者选择其他功能,而菜单提供的功能又这么多,说明我们在main函数中需要循环结构和分支选择结构。
二、代码结构(附相关源码)
1、菜单栏显示
要显示上面的菜单,而且会出现多次使用该菜单的情况,不妨将其定义成函数。(当然,宏定义也是可以,读者不妨可以试试)
// 封装函数显示界面 void showMenu() 显示菜单
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;
}
2、功能选择
前面提到了这一块的思路:循环+分支选择
// 在main函数中调用函数
int main()
{
// 声明选择变量
int select = 0;
// 创建通讯录结构体
Address_List adl;
// 初始化通讯录人数
adl.m_size = 0;
while (1)
{
// 菜单调用
showMenu();
// 输入选择
cin >> select;
// switch 多分支结构
switch (select)
{
case 1: // 1、添加联系人
addPerson(&adl);
break;
case 2: // 2、显示联系人
showPerson(&adl);
break;
case 3: // 3、删除联系人
//{
// /*string name;
// cout << "请输入删除联系人姓名: " << endl;
// cin >> name;*/
// /*if (isExist(&adl, name) != -1)
// {
// cout << "找到联系人" << endl;
// }
// else
// {
// cout << "查无此人" << endl;
// }*/
//}
deletePerson(&adl);
break;
case 4: // 4、查找联系人
findPerson(&adl);
break;
case 5: // 5、修改联系人
modifyPerson(&adl);
break;
case 6: // 6、清空联系人
cleanPerson(&adl);
break;
case 0: // 0、推出通讯录
cout << "欢迎下次使用" << endl;
system("pause");
return 0;
default:
break;
}
}
system("pause");
return 0;
}
其中的一些自定义的结构体变量,在此有相关代码
// 定义联系人结构体
struct Person
{
// 姓名
string m_name;
// 性别 1表示男, 2 表示女
int m_gender;
// 年龄
int m_age;
// 手机号码
string m_phone_number;
// 地址
string m_address;
};
// 定义通讯录结构体
struct Address_List
{
// 通讯录中保存的联系人数组
Person personArr[MAX];
// 通讯录中记录当前联系人人数
int m_size;
};
结构体变量有“联系人”结构体和“通讯录”结构体。前者用来记录联系人的信息,后者用于存储联系人结构体。
3、添加联系人
联系人的添加涉及到了数据的输入,存储。
而存储是有限的,对通讯录数组的最大容量有限制。因此在添加联系人时要先判断通讯录是否满了。
// 1、添加联系人
void addPerson(Address_List* adl)
{
// 判断通讯录是否已经满了
if (adl->m_size == MAX)
{
cout << "通讯录已满,无法添加!" << endl;
}
// 添加
else
{
// 姓名
string name;
cout << "请输入姓名: " << endl;
cin >> name;
adl->personArr[adl->m_size].m_name = name;
// 性别
int gender;
cout << "请输入性别: " << endl;
cout << "1——男" << endl;
cout << "2——女" << endl;
while (1)
{
cin >> gender;
if (gender == 1 || gender == 2)
{
adl->personArr[adl->m_size].m_gender = gender;
break;
}
cout << "错误输入,请重新输入!" << endl;
}
// 年龄
int age;
cout << "请输入年龄: " << endl;
while (1)
{
cin >> age;
if (age >= 0 && age <= 150)
{
adl->personArr[adl->m_size].m_age = age;
break;
}
cout << "错误输入,请重新输入!" << endl;
}
// 手机号码
string phonenum;
cout << "请输入手机号码: " << endl;
while (1)
{
cin >> phonenum;
// 声明变量,用于检验手机号码是否输入正确
int b = 0;
int len = phonenum.length();
for (int i = 0; i < len; i++)
{
if (phonenum[i] >= 48 && phonenum[i] <= 57)
{
continue;
}
b = 1;
break;
}
if (b)
{
cout << "手机号码输入错误,请重新输入: " << endl;
}
else
{
adl->personArr[adl->m_size].m_phone_number = phonenum;
break;
}
}
// 地址
string address;
cout << "请输入联系人地址: " << endl;
cin >> address;
adl->personArr[adl->m_size].m_address = address;
// 提示输入成功
cout << "添加成功!" << endl;
// 通讯录联系人数量增加(更新)
adl->m_size++;
system("pause"); // 请按任意键继续
system("cls"); // 清屏操作
}
}
这其中在输入上有一些考量与限制,比如性别,手机号码。
4、显示联系人
思路:先判断通讯录是否为空,不空才输出联系人信息。
// 2、显示联系人
void showPerson(Address_List* adl)
{
// 判断通讯录是否为空 如果为空,则输出提示信息,否则,输出联系人信息
if (adl->m_size == 0)
{
cout << "当前通讯录记录为空" << endl;
}
else
{
for (int i = 0; i < adl->m_size; i++)
{
cout << "姓名: " << adl->personArr[i].m_name << "\t"
<< "性别: " << (adl->personArr[i].m_gender == 1 ? "男" : "女") << "\t"
<< "年龄: " << adl->personArr[i].m_age << "\t"
<< "手机号码: " << adl->personArr[i].m_phone_number << "\t"
<< "地址: " << adl->personArr[i].m_address << endl;
}
}
system("pause");
system("cls");
}
5、删除联系人
思路类似,需要先判断联系人是否在通讯录中。
然后,对删除操作的思路其实就是做循环让后面的联系人信息覆盖要删除的信息,这种思路在作者的上一篇blog即编程题总结中的第一题数组原地去重的思路类似。
删除后,会更新通讯录中联系人人数。
// 3、删除联系人
// 检测联系人是否存在, 如果存在返回联系人在数组中的索引,不存在返回-1
int isExist(Address_List* adl, string name)
{
// 遍历通讯录
for (int i = 0; i < adl->m_size; i++)
{
// 找到了
if (adl->personArr[i].m_name == name)
{
return i;
}
}
// 没找到
return -1;
}
// 删除函数
void deletePerson(Address_List* adl)
{
cout << "请输入你要删除的联系人: " << endl;
string name;
cin >> name;
int ret = isExist(adl, name);
if (ret == -1)
{
cout << "查无此人" << endl;
}
else
{
for (int i = ret; i < adl->m_size; i++)
{
adl->personArr[i] = adl->personArr[i + 1];
}
adl->m_size--;
cout << "删除成功!" << endl;
}
system("pause");
system("cls");
}
6、查找联系人
与删除类似,判断联系人是否在通讯录,同时利用判断函数返回的联系人在通讯录数组的索引。
// 4、查找联系人
void findPerson(Address_List* adl)
{
cout << "请输入你要查找的联系人: " << endl;
string name;
cin >> name;
int ret = isExist(adl, name);
if (ret != -1)
{
cout << "姓名: " << adl->personArr[ret].m_name << "\t"
<< "性别: " << (adl->personArr[ret].m_gender == 1 ? "男" : "女") << "\t"
<< "年龄: " << adl->personArr[ret].m_age << "\t"
<< "手机号码: " << adl->personArr[ret].m_phone_number << "\t"
<< "地址: " << adl->personArr[ret].m_address << endl;
}
else
{
cout << "查无此人!" << endl;
}
system("pause");
system("cls");
}
7、修改联系人
思路类似,同时由于涉及到信息的输入,所以也要考虑前面的添加联系人需要考虑的因素。
// 5、修改联系人
void modifyPerson(Address_List* adl)
{
cout << "请输入你要修改的联系人" << endl;
string name;
cin >> name;
int ret = isExist(adl, name);
if (ret == -1)
{
cout << "查无此人" << endl;
}
else
{
// 姓名
cout << "请输入姓名: " << endl;
cin >> name;
adl->personArr[ret].m_name = name;
// 性别
cout << "请输入性别: " << endl;
cout << "1——男" << endl
<< "2——女" << endl;
int gender;
while (1)
{
cin >> gender;
if (gender == 1 || gender == 2)
{
adl->personArr[ret].m_gender = gender;
break;
}
cout << "性别输入错误,请重新输入" << endl;
}
// 年龄
cout << "请输入年龄: " << endl;
int age;
while (1)
{
cin >> age;
if (age >= 0 && age <= 150)
{
adl->personArr[ret].m_age = age;
break;
}
cout << "年龄输入错误,请重新输入" << endl;
}
// 手机号码
cout << "请输入联系人号码: " << endl;
string phonenum;
while (1)
{
cin >> phonenum;
// 声明变量,用于检验手机号码是否输入正确
int b = 0;
int len = phonenum.length();
for (int i = 0; i < len; i++)
{
if (phonenum[i] >= 48 && phonenum[i] <= 57)
{
continue;
}
b = 1;
break;
}
if (b)
{
cout << "手机号码输入错误,请重新输入: " << endl;
}
else
{
adl->personArr[adl->m_size].m_phone_number = phonenum;
break;
}
}
// 地址
cout << "请输入地址: " << endl;
string address;
cin >> address;
adl->personArr[ret].m_address = address;
}
system("pause");
system("cls");
}
8、清空联系人
就是清空通讯录,前面提到的用来记录的通讯录中联系人数量的变量此时做赋值为0的操作,而不是真的一个个去把这些信息去除掉,思路和删除联系人类似。
// 6、清空联系人
void cleanPerson(Address_List* adl)
{
cout << "是否需要清空通讯录" << endl
<< "1——是\t2——否" << endl;
int n = 0;
while (true)
{
cin >> n;
if (n == 1)
{
adl->m_size = 0;
cout << "通讯录已清空" << endl;
break;
}
else if (n == 2)
{
break;
}
else
{
cout << "错误输入,请重新输入" << endl;
}
}
system("pause");
system("cls");
}
二、小知识
1、数据结构数组和数据结构的嵌套
如图,这种在一个数据结构的定义中提到另一个数据结构就是数据结构的嵌套,同时personArr也是一种数据结构数组。
2、指针使用
在本文中的大量代码,尤其是函数的参数都使用了数据结构指针,为什么不适用值传递,而是选择指针传递呢?
原因有二:其一,指针传递可以直接对通讯录联系人等信息的修改;其二,当通讯录中联系人太多的时候会导致函数在调用的时候使用内存太大。这无疑会导致函数的栈帧空间过大,会使得这个程序的空间复杂度过大。而指针的好处在于,c++对于指针,其内存被定义为4个字节。无论这个指针的类型是什么。在函数调用,内存可以有极大的节省。
3、清屏操作
system(“pause”) 用于任意键继续
system(“cls”) 用于清屏操作
使用清屏主要是因为,在每次使用一次功能菜单后的信息显示过多,而且当使用次数过多时会显得很不美观。清屏配合循环结构在显示上会很棒。
三、程序源码
#include <iostream>
#include<string>
using namespace std;
// 定义通讯录容量
#define MAX 1000
// 定义联系人结构体
struct Person
{
// 姓名
string m_name;
// 性别 1表示男, 2 表示女
int m_gender;
// 年龄
int m_age;
// 手机号码
string m_phone_number;
// 地址
string m_address;
};
// 定义通讯录结构体
struct Address_List
{
// 通讯录中保存的联系人数组
Person personArr[MAX];
// 通讯录中记录当前联系人人数
int m_size;
};
// 封装函数显示界面 void showMenu() 显示菜单
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;
}
// 1、添加联系人
void addPerson(Address_List* adl)
{
// 判断通讯录是否已经满了
if (adl->m_size == MAX)
{
cout << "通讯录已满,无法添加!" << endl;
}
// 添加
else
{
// 姓名
string name;
cout << "请输入姓名: " << endl;
cin >> name;
adl->personArr[adl->m_size].m_name = name;
// 性别
int gender;
cout << "请输入性别: " << endl;
cout << "1——男" << endl;
cout << "2——女" << endl;
while (1)
{
cin >> gender;
if (gender == 1 || gender == 2)
{
adl->personArr[adl->m_size].m_gender = gender;
break;
}
cout << "错误输入,请重新输入!" << endl;
}
// 年龄
int age;
cout << "请输入年龄: " << endl;
while (1)
{
cin >> age;
if (age >= 0 && age <= 150)
{
adl->personArr[adl->m_size].m_age = age;
break;
}
cout << "错误输入,请重新输入!" << endl;
}
// 手机号码
string phonenum;
cout << "请输入手机号码: " << endl;
while (1)
{
cin >> phonenum;
// 声明变量,用于检验手机号码是否输入正确
int b = 0;
int len = phonenum.length();
for (int i = 0; i < len; i++)
{
if (phonenum[i] >= 48 && phonenum[i] <= 57)
{
continue;
}
b = 1;
break;
}
if (b)
{
cout << "手机号码输入错误,请重新输入: " << endl;
}
else
{
adl->personArr[adl->m_size].m_phone_number = phonenum;
break;
}
}
// 地址
string address;
cout << "请输入联系人地址: " << endl;
cin >> address;
adl->personArr[adl->m_size].m_address = address;
// 提示输入成功
cout << "添加成功!" << endl;
// 通讯录联系人数量增加(更新)
adl->m_size++;
system("pause"); // 请按任意键继续
system("cls"); // 清屏操作
}
}
// 2、显示联系人
void showPerson(Address_List* adl)
{
// 判断通讯录是否为空 如果为空,则输出提示信息,否则,输出联系人信息
if (adl->m_size == 0)
{
cout << "当前通讯录记录为空" << endl;
}
else
{
for (int i = 0; i < adl->m_size; i++)
{
cout << "姓名: " << adl->personArr[i].m_name << "\t"
<< "性别: " << (adl->personArr[i].m_gender == 1 ? "男" : "女") << "\t"
<< "年龄: " << adl->personArr[i].m_age << "\t"
<< "手机号码: " << adl->personArr[i].m_phone_number << "\t"
<< "地址: " << adl->personArr[i].m_address << endl;
}
}
system("pause");
system("cls");
}
// 3、删除联系人
// 检测联系人是否存在, 如果存在返回联系人在数组中的索引,不存在返回-1
int isExist(Address_List* adl, string name)
{
// 遍历通讯录
for (int i = 0; i < adl->m_size; i++)
{
// 找到了
if (adl->personArr[i].m_name == name)
{
return i;
}
}
// 没找到
return -1;
}
// 删除函数
void deletePerson(Address_List* adl)
{
cout << "请输入你要删除的联系人: " << endl;
string name;
cin >> name;
int ret = isExist(adl, name);
if (ret == -1)
{
cout << "查无此人" << endl;
}
else
{
for (int i = ret; i < adl->m_size; i++)
{
adl->personArr[i] = adl->personArr[i + 1];
}
adl->m_size--;
cout << "删除成功!" << endl;
}
system("pause");
system("cls");
}
// 4、查找联系人
void findPerson(Address_List* adl)
{
cout << "请输入你要查找的联系人: " << endl;
string name;
cin >> name;
int ret = isExist(adl, name);
if (ret != -1)
{
cout << "姓名: " << adl->personArr[ret].m_name << "\t"
<< "性别: " << (adl->personArr[ret].m_gender == 1 ? "男" : "女") << "\t"
<< "年龄: " << adl->personArr[ret].m_age << "\t"
<< "手机号码: " << adl->personArr[ret].m_phone_number << "\t"
<< "地址: " << adl->personArr[ret].m_address << endl;
}
else
{
cout << "查无此人!" << endl;
}
system("pause");
system("cls");
}
// 5、修改联系人
void modifyPerson(Address_List* adl)
{
cout << "请输入你要修改的联系人" << endl;
string name;
cin >> name;
int ret = isExist(adl, name);
if (ret == -1)
{
cout << "查无此人" << endl;
}
else
{
// 姓名
cout << "请输入姓名: " << endl;
cin >> name;
adl->personArr[ret].m_name = name;
// 性别
cout << "请输入性别: " << endl;
cout << "1——男" << endl
<< "2——女" << endl;
int gender;
while (1)
{
cin >> gender;
if (gender == 1 || gender == 2)
{
adl->personArr[ret].m_gender = gender;
break;
}
cout << "性别输入错误,请重新输入" << endl;
}
// 年龄
cout << "请输入年龄: " << endl;
int age;
while (1)
{
cin >> age;
if (age >= 0 && age <= 150)
{
adl->personArr[ret].m_age = age;
break;
}
cout << "年龄输入错误,请重新输入" << endl;
}
// 手机号码
cout << "请输入联系人号码: " << endl;
string phonenum;
while (1)
{
cin >> phonenum;
// 声明变量,用于检验手机号码是否输入正确
int b = 0;
int len = phonenum.length();
for (int i = 0; i < len; i++)
{
if (phonenum[i] >= 48 && phonenum[i] <= 57)
{
continue;
}
b = 1;
break;
}
if (b)
{
cout << "手机号码输入错误,请重新输入: " << endl;
}
else
{
adl->personArr[adl->m_size].m_phone_number = phonenum;
break;
}
}
// 地址
cout << "请输入地址: " << endl;
string address;
cin >> address;
adl->personArr[ret].m_address = address;
}
system("pause");
system("cls");
}
// 6、清空联系人
void cleanPerson(Address_List* adl)
{
cout << "是否需要清空通讯录" << endl
<< "1——是\t2——否" << endl;
int n = 0;
while (true)
{
cin >> n;
if (n == 1)
{
adl->m_size = 0;
cout << "通讯录已清空" << endl;
break;
}
else if (n == 2)
{
break;
}
else
{
cout << "错误输入,请重新输入" << endl;
}
}
system("pause");
system("cls");
}
// 在main函数中调用函数
int main()
{
// 声明选择变量
int select = 0;
// 创建通讯录结构体
Address_List adl;
// 初始化通讯录人数
adl.m_size = 0;
while (1)
{
// 菜单调用
showMenu();
// 输入选择
cin >> select;
// switch 多分支结构
switch (select)
{
case 1: // 1、添加联系人
addPerson(&adl);
break;
case 2: // 2、显示联系人
showPerson(&adl);
break;
case 3: // 3、删除联系人
//{
// /*string name;
// cout << "请输入删除联系人姓名: " << endl;
// cin >> name;*/
// /*if (isExist(&adl, name) != -1)
// {
// cout << "找到联系人" << endl;
// }
// else
// {
// cout << "查无此人" << endl;
// }*/
//}
deletePerson(&adl);
break;
case 4: // 4、查找联系人
findPerson(&adl);
break;
case 5: // 5、修改联系人
modifyPerson(&adl);
break;
case 6: // 6、清空联系人
cleanPerson(&adl);
break;
case 0: // 0、推出通讯录
cout << "欢迎下次使用" << endl;
system("pause");
return 0;
default:
break;
}
}
system("pause");
return 0;
}
附上一句,这次使用了数组,读者有兴趣的话不妨使用链表来存储。