- 首先是主函数,将整个函数分模块写再封装,这是规范的做法,方便后续修改,主函数中也更加简洁美观,因为通讯录管理系统涉及多种功能,于是用分支语句switch是最好的,然后在把实现不同功能的事件调用相应功能的函数。
注意:创建一个模块的步骤是:
首先:先创建头文件(.h),在头文件中写好该函数的声明,并包含所需的头文件,如:#include<string>,这样的好处就是你的源文件中只需要包含相关函数的头文件
然后:创建对应的源文件(如Show_menu.cpp),记得要包含对应的头文件(Show_menu.h)
最后:还要在头文件中也包含上该函数的头文件。
尖括号<>用于包含标准库和系统头文件;
双引号""用于包含自定义头文件或者项目内部的包含,此处应该是项目内部头文件的包含。
#include<iostream>
#include"Show_menu.h"
#include<cstdlib>
#include<string>
#include"Add_person.h"
#include"Find_person.h"
#include"Show_person.h"
#include"Delete_person.h"
#include"Modify_person.h"
#include"Empty_person.h"
using namespace std;
int main()
{
person* last_node;
//last_node = (person*)malloc(sizeof(person));
last_node = nullptr;
int select;
while (1)
{
Show_menu();//显示菜单
cin >> select;
switch (select)
{
case 1://添加联系人
Add_person(&last_node);
break;
case 2://显示联系人
Show_person(&last_node);
break;
case 3://删除联系人
Delete_person(&last_node);
break;
case 4://查找联系人
Find_person(&last_node);
break;
case 5://修改联系人
Modify_person(&last_node);
break;
case 6://清空联系人
Empty_person(&last_node);
break;
case 0://退出通讯录
cout << "欢迎下次使用" << endl;
system("pause");
return 0;
break;
default:
break;
}
}
return 0;
}
- std是C++标准库的命名空间,需要注意的是,使用 using namespace std; 语句可以在当前作用域内导入整个 std 命名空间,这样就无需在每个名称前添加 std:: 前缀。然而,这种方式可能导致命名冲突或歧义,因此在大型项目或避免与其他命名空间发生冲突的情况下,最好避免在头文件中使用 using namespace std;,而是显式添加 std:: 前缀来引用标准库中的成员。
- 第一步——显示一个菜单,我们才能够在运行时知道如何选择功能,于是需要编写一个显示菜单的函数。
首先添加一个头文件(Show_menu.h)
#ifndef SHOW_MENU_H
#define SHOW_MENU_H
#include<iostream>
using namespace std;
void Show_menu();
#endif
然后,添加源文件(Show_menu.cpp)
#include"Show_menu.h"
/*显示菜单*/
void Show_menu()
{
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;
}
这样,显示菜单的部分就完成了,而我们所需要的是当我们不输入0表示退出通讯录的时候,我们能够一直使用菜单功能,于是我们将代码放在一个无限循环的while里,只有我们输入0,主动要退出通讯录的时候,才能够退出。
- 第二步——添加联系人,头文件:
#ifndef ADD_PERSON_H
#define ADD_PERSON_H
#include<string>
#include<iostream>
struct person
{
std::string Name;//姓名
std::string Phone;//电话
std::string Sex;//性别
int Age;//年龄
std::string Address;//地址
struct person* node;//指针域
};
void Add_person(person** last_node);
#endif
在 C++ 中,头文件通常包含函数、类、结构体的声明、宏定义等内容,这些内容可能会被多个源文件引用。当一个头文件在多个源文件中被重复包含时,可能会导致重定义错误或其他编译错误。
条件编译指令块可以确保头文件只被包含一次,以避免这些错误。
以下是 #ifndef 到 #endif 的基本作用和意义:
#ifndef(如果未定义):检查指定的标识符是否未定义(通常是一个宏定义),如果未定义,则执行下面的代码块。
#define:在此处定义指定的标识符(通常是一个宏定义),将其置为已定义状态。
头文件的实际内容:在 #ifndef 和 #endif 之间,包含头文件的实际内容,如函数、类、结构体的声明等。
#endif:结束条件编译指令块。
通过在头文件的开始部分使用 #ifndef 和 #define,可以确保在多次包含该头文件时,只有第一次包含会真正生效,后续的包含会被忽略。
然后,添加联系人源文件:
#include"Add_person.h"
//#include<string>
//#include<iostream>
using namespace std;
void Add_person(person** last_node)
{
int i, n;
cout << "请输入要添加的联系人的个数" << endl;
cin >> n;
for (i = 0; i < n; i++)
{
cout << "请输入第" << i + 1 << "个联系人的信息" << endl;
person* p;
p = new person;
cout << "请输入要添加的练习人的信息\n" << "姓名" << endl;
cin >> p->Name;
cout << "电话" << endl;
cin >> p->Phone;
cout << "性别" << endl;
cin >> p->Sex;
cout << "年龄" << endl;
cin >> p->Age;
cout << "地址" << endl;
cin >> p->Address;
p->node = (*last_node);
(*last_node) = p;
}
std::cout << "新的联系人已经添加完成" << std::endl;
}
person** last_node 是一个指向指针的指针,它的意义是允许通过该指针来修改指针的值。
在上述代码中,person** last_node 是用来存储一个指向指针 person* 的指针。它的作用是在函数之间传递一个指针,并允许函数修改指针指向的内容。
通过使用指向指针的指针,你可以在函数内部修改传递给函数的指针的值。这是因为指针本身也是一种数据类型,它可以被传递给函数,并在函数内部修改它所指向的内容。
这里我是意识到,加std是一个安全的做法,于是,一半加了一半又没加,不过后面的模块都加了,添加联系人的部分是我认为最麻烦的,因为我一开始有点不知道,person这个结构体应该在主函数定义还是添加联系人函数里面定义,中途出了好多问题,我一开始是在主函数定义,但是又不知道怎么传到其他函数中去了,因为所有模块的形参都是一个指向person*的指针,后来问题太多原本打算放弃了就不分模块写了,不过最后我是先在主函数中写好声明和定义,然后在一部分一部分地搬到Add_person.h与Add_person.cpp中,然后我原本也是将结构体person的声明和定义分开写的,不过出错了,然后我就直接把定义写在源文件中了。
- 第三步——删除联系人,同样先是源文件,后面的模块的源文件就比较简单了,基本上都是声明,还要包含Add_person.h头文件,因为该头文件中有person结构体的定义。
#ifndef DLETE_PERSON_H
#define DLETE_PERSON_H
#include"Add_person.h"
//删除联系人
void Delete_person(person** last_node);
#endif
然后是,删除联系人源文件,需要注意的是,因为通讯录是由链表实现的,所以一定要记得,删除某个结点的时候,要原本指向该结点的指针在这个结点被删除之前指向正确的地方,例如:让它上一个结点指向它的下一个结点;还有就是,要么就直接用尾指针将联系人链表遍历,然后定义一个变量用于保存尾指针最开始的值,然后如果要删除的是最后一个结点,那么必须修改一开始用于保存尾指针的初值的指针变量save_last_node,最后运行结束时将尾指针的值恢复到最后一个结点;要么就重新定义一个指针变量代替它,用于遍历。
#include"Delete_person.h"
//删除联系人
void Delete_person(person** last_node)
{
int count = 0;
int select;
person* delete_node = nullptr;//保存要删除的联系人的空间地址,用于释放该结点,即删除
person* save_last_node = nullptr;
person* previous_node = nullptr;;
std::string Name;
save_last_node = *last_node;
std::cout << "请输入要删除的联系人的姓名" << std::endl;
std::cin >> Name;
while ((*last_node) != nullptr)
{
if ((*last_node)->Name == Name)
{
std::cout << "查找到第" << count + 1 << "个联系人" << Name << "的信息" << std::endl;
std::cout << "姓名: " << (*last_node)->Name << std::endl;
std::cout << "电话: " << (*last_node)->Phone << std::endl;
std::cout << "性别: " << (*last_node)->Sex << std::endl;
std::cout << "年龄: " << (*last_node)->Age << std::endl;
std::cout << "地址: " << (*last_node)->Address << std::endl;
count++;
std::cout << "请选择是否删除\n" << "1.是 0.否" << std::endl;
std::cin >> select;
if (select == 1)
{
if (count == 0)
{
//如果要删除的是最后一个结点,那么必须修改一开始用于保存尾指针的初值的指针变量save_last_node
save_last_node = save_last_node->node;
}
delete_node = (*last_node);
previous_node->node = (*last_node)->node;
(*last_node) = (*last_node)->node;
delete delete_node;
std::cout << "联系人" << Name << "的信息已经删除" << std::endl;
}
else
{
previous_node = (*last_node);
(*last_node) = (*last_node)->node;
}
}
else
{
previous_node = (*last_node);
(*last_node) = (*last_node)->node;
}
}
if (count == 0)
{
std::cout << "未在通讯录中查找到 " << Name << "的信息" << std::endl;
}
(*last_node) = save_last_node;
}
- 第四步——查找联系人,头文件:
#ifndef FIND_PERSON_H
#define FIND_PERSON
#include"Add_person.h"
//查找联系人
void Find_person(person** last_node);
#endif
然后,查找联系人源文件:
#include"Find_person.h"
//查找联系人
void Find_person(person** last_node)
{
int count = 0;
person* save_last_node;
person* next_node;
std::string Name;
save_last_node = *last_node;
std::cout << "请输入要查找的联系人的姓名" << std::endl;
std::cin >> Name;
while ((*last_node) != nullptr)
{
if ((*last_node)->Name == Name)
{
std::cout << "查找到第" << count + 1 << "个联系人" << Name << "的信息" << std::endl;
std::cout << "姓名: " << (*last_node)->Name << std::endl;
std::cout << "电话: " << (*last_node)->Phone << std::endl;
std::cout << "性别: " << (*last_node)->Sex << std::endl;
std::cout << "年龄: " << (*last_node)->Age << std::endl;
std::cout << "地址: " << (*last_node)->Address << std::endl;
count++;
}
(*last_node) = (*last_node)->node;
}
if (count == 0)
{
std::cout << "未在通讯录中查找到 " << Name << "的信息" << std::endl;
}
(*last_node) = save_last_node;
}
- 第五步——修改联系人,头文件:
#ifndef MODIFY_PERSON_H
#define MODIFY_PERSON_H
#include"Add_person.h"
//修改联系人
int Modify_person(person** last_node);
#endif
然后,修改联系人源文件:,这里需要注意的是,当修改完之后,然后选择结束时,就直接退出了函数,回到主函数了,但是这不是我想要的结果,我需要的是结束对当前的联系人的修改,进入下一个同名的联系人进行修改,而这里除了goto我想不到其他好办法了,当然goto是会让代码理解起来难度加大,如果有更好的方法可以在评论区告诉我。
#include"Modify_person.h"
//修改联系人
int Modify_person(person** last_node)
{
int count = 0;
int select;
person* modify_node = nullptr;
person* save_last_node = nullptr;
person* previous_node = nullptr;;
std::string Name;
save_last_node = *last_node;
std::cout << "请输入要修改的联系人的姓名" << std::endl;
std::cin >> Name;
std::cout << (*last_node)->Name << std::endl;
while ((*last_node) != nullptr)
{
if ((*last_node)->Name == Name)
{
std::cout << "查找到第" << count + 1 << "个联系人" << Name << "的信息" << std::endl;
std::cout << "姓名: " << (*last_node)->Name << std::endl;
std::cout << "电话: " << (*last_node)->Phone << std::endl;
std::cout << "性别: " << (*last_node)->Sex << std::endl;
std::cout << "年龄: " << (*last_node)->Age << std::endl;
std::cout << "地址: " << (*last_node)->Address << std::endl;
count++;
std::cout << "请选择是否修改\n" << "1.是 0.否" << std::endl;
std::cin >> select;
if (select == 1)
{
while (select != 0)
{
std::cout << "请选择要修改的内容:1.姓名 2.电话 3.性别 4.年龄 5.地址 6.退出对当前联系人的修改 0.结束修改" << std::endl;
std::cin >> select;
switch (select)
{
case 1:
std::cout << "请重新输入该联系人的姓名" << std::endl;
std::cin >> (*last_node)->Name;
std::cout << "该联系人的姓名已经更改为 " << (*last_node)->Name << std::endl;
break;
case 2:
std::cout << "请重新输入该联系人的电话" << std::endl;
std::cin >> (*last_node)->Phone;
std::cout << "该联系人的电话已经更改为 " << (*last_node)->Phone << std::endl;
break;
case 3:
std::cout << "请重新输入该联系人的性别" << std::endl;
std::cin >> (*last_node)->Sex;
std::cout << "该联系人的性别已经更改为 " << (*last_node)->Sex << std::endl;
break;
case 4:
std::cout << "请重新输入该联系人的年龄" << std::endl;
std::cin >> (*last_node)->Age;
std::cout << "该联系人的年龄已经更改为 " << (*last_node)->Age << std::endl;
break;
case 5:
std::cout << "请重新输入该联系人的地址" << std::endl;
std::cin >> (*last_node)->Address;
std::cout << "该联系人的地址已经更改为 " << (*last_node)->Address << std::endl;
break;
case 6:
goto next;//直接下一位
case 0:
(*last_node) = save_last_node;
return 0;
}
}
}
else
{
next://结束对当前联系人的修改,遍历下一位
previous_node = (*last_node);
(*last_node) = (*last_node)->node;
}
}
else
{
previous_node = (*last_node);
(*last_node) = (*last_node)->node;
}
}
if (count == 0)
{
std::cout << "未在通讯录中查找到 " << Name << "的信息" << std::endl;
}
(*last_node) = save_last_node;
}
第六步——清空联系人,头文件:
#ifndef EMPTY_PERSON_H
#define EMPTY_PERSON_H
#include"Add_person.h"
//清空联系人
void Empty_person(person** last_node);
#endif
源文件:
#include"Empty_person.h"
//清空联系人
void Empty_person(person** last_node)
{
person* delete_node = nullptr;
person* save_last_node = nullptr;
save_last_node = *last_node;
while ((*last_node) != nullptr)
{
delete_node = (*last_node);
//previous_node->node = (*last_node)->node;
(*last_node) = (*last_node)->node;
delete delete_node;
}
std::cout << "通讯录中的全部联系人信息已经清空" << std::endl;
}
补充:注意到在删除联系人与清空联系人还有添加联系人时,动态申请空间用的是new,释放空间用的是delete,在C++中也可以用malloc与free,但是new 和 delete 是 C++ 提供的更高级、更安全的内存管理机制,可以直接与对象关联,自动调用构造函数和析构函数。而 malloc 和 free 是 C 语言的标准库函数,功能相对简单,需要手动进行内存管理和对象的初始化和清理。在 C++ 中,推荐使用 new 和 delete 来进行动态内存管理,而在 C 语言或混合 C/C++ 的项目中,可以使用 malloc 和 free。
总结:
以上就是我本人用C++编写的通讯录管理系统,感受最深的就是模块化编程确实让程序变得更加简洁且易懂,也方便修改,当哪一功能出问题时可以直接定位到相关的源文件与头文件,还有就是,在这种许多文件组成的项目中,一定要明确每一部分的归属,所以,这种时候在每一个数据库函数前加上它的命名空间是一个安全的做法,还有在每一个模块的头文件加上#ifndef、#defne、#endift也是一个保护程序的好习惯。