C++利用单向链表实现通讯录管理系统完整代码
介绍使用C++和Windows API构建基于链表的通讯录管理系统的完整方案,包含节点定义、动态内存分配及基础CRUD操作。
项目概述与设计思路
通讯录管理的核心在于数据的持久化存储(在运行期间)与快速检索。本方案采用C++作为开发语言,利用单向链表(Singly Linked List)作为底层数据结构。相比数组,链表无需预先分配固定大小的内存,能够根据新增联系人数量动态扩展存储空间。
该系统的目标是在Windows环境下运行,支持以下核心业务逻辑:
- 存储结构:使用
struct定义包含姓名、电话、生日、地址及录入时间的信息块,并封装为链表节点。 - 数据处理:实现增(Insert)、删(Delete)、改(Update)、查(Search)四种基本操作。
- 特殊功能:集成Windows API
GetLocalTime自动记录联系人添加时的系统日期。
第1步:定义基础数据结构与类型
首先引入必要的IO流、字符串处理及系统API头文件。为了保持代码清晰,我们将联系人的基本信息抽象为一个独立的 A 结构体,并将链表节点定义为 LNode。
#include <iostream>
#include <string>
#include <malloc.h> // 用于动态内存分配 new
#include <windows.h> // 调用 Windows API 获取系统时间
#include <sstream> // 用于数据类型转换 using std::stringstream
using namespace std;
#define OK 1
#define ERROR 0
typedef int Status;
// 1. 定义基础联系人员结构体
typedef struct info {
string number; // 电话号码
string date; // 插入日期
string name; // 姓名
string adress; // 地址
string birthday; // 生日
} A;
// 2. 定义链表节点结构体
typedef struct LNode {
A data; // 数据域
struct LNode *next; // 指针域
} LNode, *LinkList;
第2步:初始化链表
在使用链表前必须对其进行初始化,即创建一个不存有效数据的头节点(Head Node),并将其指针域设为NULL。
Status InitList(LinkList &L) {
L = new LNode; // 为头节点分配内存
L->next = NULL; // 初始状态为空表
return OK;
}
第3步:实现前插法插入节点(新建联系人)
系统需要接收用户输入的各字段信息。考虑到Windows系统返回的时间数据类型为浮点型或其他数值型,这里先提供一个辅助函数将数字转换为字符串。
// 辅助函数:double转string
string doubleToString(double num) {
stringstream ss;
string str;
ss << num;
ss >> str;
return str;
}
// 在链表头部插入新节点
Status ListInsert(LinkList &L, string name, string adress, string birthday, string date, string number) {
LinkList p = new LNode;
p->data.name = name;
p->data.adress = adress;
p->data.date = date;
p->data.birthday = birthday;
p->data.number = number;
// 前插法:新节点指向原首元节点,头结点指向新节点
p->next = L->next;
L->next = p;
return OK;
}
注意:插入时需要校验手机号长度是否为11位,并检查姓名是否已存在于系统中以防止重复。
第4步:多条件查询逻辑设计
为了提高程序的灵活性,查询函数接受一个标识符 i 和搜索内容 e。例如当 i=1 时按姓名查,i=5 时按电话查。这是一种典型的复用代码模式,避免了写五个相似的搜索函数。
LinkList SearchElemChar(LinkList L, int i, string e) {
while (L != NULL) {
bool match = false;
if (i == 1 && L->data.name == e) match = true;
else if (i == 2 && L->data.birthday == e) match = true;
else if (i == 3 && L->data.adress == e) match = true;
else if (i == 4 && L->data.date == e) match = true;
else if (i == 5 && L->data.number == e) match = true;
if (match) {
return L; // 找到则返回当前节点指针
} else {
L = L->next;
}
}
cout << "未查到数据!" << endl;
return NULL;
}
第5步:删除与遍历显示
删除操作是链表中最易出错的部分。由于单链表只能向后指,要删除节点D,必须先拿到其前驱节点P(前插法的逻辑中需要注意)。此处通过 SearchElemBefore 寻找目标节点的前一个位置进行unlink。
// 查找目标值的前驱节点(用于删除)
LinkList SearchElemBefore(LinkList L, string e) {
LinkList P = L;
while (L->next != NULL) {
if (L->next->data.name == e) {
return P; // 找到了前一个节点
}
L = L->next;
}
return NULL;
}
void foreachelem(LinkList L) {
if (L->next == NULL) {
cout << "通讯录里还没有联系人!" << endl;
return;
}
while (L->next != NULL) {
L = L->next;
cout << "--- 联系人信息 ---" << endl;
cout << "姓名: " << L->data.name << endl;
cout << "电话: " << L->data.number << endl;
cout << "地址: " << L->data.adress << endl;
cout << "生日: " << L->data.birthday << endl;
cout << "录入日: " << L->data.date << endl;
}
}
第6步:主界面交互与系统流程集成
主函数中通过循环等待指令输入,并调用上述封装好的函数。利用 system("cls") 实现清屏刷新界面效果。
int ShowMenu() {
int a;
system("cls");
cout << "========== 通讯录管理系统 ==========" << endl;
cout << "-----请根据功能输入对应的序号--------" <<endl;
cout << "-----------1.新建联系人--------------" <<endl;
cout << "--------2.查看所有联系人-------------" <<endl;
cout << "--------3.修改选定联系人-------------" <<endl;
cout << "--------4.查询选定联系人-------------" <<endl;
cout << "--------5.删除选定联系人-------------" <<endl;
cout << "------------6.退出系统---------------" <<endl;
cout << "请输入序号并按回车进入:" ;
cin >> a;
return a;
}
int main() {
int choice;
LinkList L;
InitList(L);
while (choice != 6) {
choice = ShowMenu();
switch (choice) {
case 1: { // 新建
string name, addr, birth, phone;
cout << "请输入姓名:"; cin >> name;
// ... 省略其他输入 ...
SYSTEMTIME sys;
GetLocalTime( &sys );
string today = to_string(sys.wYear) + "-" + to_string(sys.wMonth); // 简化示例
ListInsert(L, name, addr, birth, today, phone);
break;
}
case 2: foreachelem(L); break;
// case 3, 4, 5 逻辑同前述章节
default: cout << "无效选择!" << endl;
}
system("pause");
}
return 0;
}
关键技术点说明
- 内存安全:在
ListInsert和Delete操作中,频繁涉及new分配。在实际工程级应用中,应在退出程序前递归释放所有节点内存,否则会导致内存泄漏(Memory Leak)。 - API兼容性:使用了
<windows.h>中的GetLocalTime,这意味着该代码仅能在Windows环境下编译通过。跨平台移植需替换为标准C库的时间函数。 - 健壮性:在进行修改或查询前,必须判断返回值是否非空,避免对
NULL指针解引用导致段错误(Segmentation Fault)。
以上就是C++使用链表存储实现通讯录功能管理的详细内容,更多关于C++数据结构教程的资料请关注本站其它相关文章!
免责声明:本文内容仅供参考,不代表本站观点。转载或引用请注明出处。









