本文目录
通讯录效果图
问题描述
该设计采用菜单作为应用程序的主要界面,用控制语句来改变程序执行的顺序,控制语句是实现结构化程序设计的基础。该设计的任务是利用一个简单实用的菜单,通过菜单单项进行选择,实现和完成通讯录管理中常用的几个不同的功能。通讯者所包含信息请自行设定
任务要求
菜单内容:
(0)通讯录链表的建立
(1)通讯者结点的插入
(2)通讯者结点的查询
(3)通讯者结点的删除
(4)通讯录链表的输出
(5)退出管理系统
设计要求:
使用0-5来选择菜单项,可扩展功能。
功能函数设计
5个不同功能的算法实现编程题,目的是练习利用链表结构来解决实际应用问题的能力,进一步理解和熟悉线形表的链式存储结构
设计思想
将数据存储的链表,用户交互界面,核心测试类分成三大模块。链表,界面作为头文件,引入到测试类中,实现功能的分块整合。不同模块中有具体的实现,通过接口类,暴露实现方法名称,隐藏实现细节。
功能模块
1,数据存储
以简单单向列表的形式来存储数据,链表实现类继承接口类,并实现其中的方法。
2,交互界面
通过字符画的形式构建交互界面,并将绘制界面的各个方法,封装入界面绘制工具类中,定义在“MYGUI.h”的头文件中。
3,主类功能匹配与整合
将数据存储模块“Link.h”和交互界面“MYGUI.h”载入主类中,通过子类的工具类,根据用户的不同操作匹配调用各个头文件中的方法,并整合流程与方法。
程序流程图
源码实现
【1】Link.h
#include<iostream>
#include<string>
#include<fstream>
using namespace std;
static int counts=0;//链数
static int foot;//索引
class ILink{//接口类,暴露方法,隐藏细节
protected:
virtual void add(string data)=0;
virtual int count()=0;
virtual bool isEmpty()=0;
virtual string get(int index)=0;
virtual void set(int index)=0;
virtual void toArray()=0;
virtual bool contains(string data)=0;
virtual void remove(int index)=0;
virtual void clean()=0;
virtual void fileOut(char* path)=0;
virtual void fileIn(char* path)=0;
};
class Node{//结点类
public:
string data;//结点数据
Node *next=NULL;//保留下一节点
Node(string data){
this->data=data;
}
//Node类对外支持的方法,主要使用了递归
void addNode(Node* newNode){
if(this->next==NULL){
this->next=newNode;
}else{
this->next->addNode(newNode);
}
}
void toArrayNode(){
cout<<"["<<foot++<<"]"<<this->data<<endl;
if(this->next!=NULL){
this->next->toArrayNode();
}
}
string getNode(int index){
if(foot++==index){
return this->data;
}else{
return this->next->getNode(index);
}
}
void setNode(int index,string data){
if(foot++==index){
this->data=data;
}else{
this->next->setNode(index,data);
}
}
bool containsNode(string data){
if(this->data==data){
return 1;
}else if(this->next==NULL){
return 0;
}else{
return this->next->containsNode(data);
}
}
void removeNode(Node* previous,int index){//一定要接受指针,进行地址传递,不能进行值传递
if(foot++==index){
previous->next=this->next;
}else if(this->next!=NULL){
this->next->removeNode(this,index);
}
}
void fileOutNode(char* path)throw (int){
ofstream file;
file.open(path,ios::app);
if(!file){
throw 0;
return ;
}
file<<this->data<<endl;
if(this->next!=NULL){
this->next->fileOutNode(path);
}else{
file.close();//关闭文件流
}
}
};
class Link:public ILink{
private:
Node *root=NULL;//初始化根节点
public:
~Link(){
delete root;
}
//Link类对外支持的方法
void add(string data){//add的两种形式:向根节点添加,向子节点添加
Node* newNode=new Node(data);
if(this->root==NULL){
this->root=newNode;
}else{
this->root->addNode(newNode);
}
counts++;//链数加一
}
int count(){
return counts;
}
bool isEmpty(){
return counts==0;
}
void toArray(){
if(this->isEmpty()){
cout<<"链表中没有数据"<<endl;
}
foot=1;
root->toArrayNode();
}
string get(int index) throw (int){
if(index>counts|index<=0){//两个判断都要进行,所以不用短路或“||”
throw 0;//索引无效
return "ERROR";
}
foot=1;//初始化索引
return this->root->getNode(index);
}
void set(int index)throw (int){
if(index>counts|index<=0){//两个判断都要进行,所以不用短路或“||”
throw 0;//索引无效
return;
}
string str[4];
cout<<"通讯者 [姓名]:";
cin>>str[0];
cout<<"通讯者 [电话]:";
cin>>str[1];
cout<<"通讯者 [QQ]:";
cin>>str[2];
str[3]="[姓名]:"+str[0]+" [电话]:"+str[1]+" [QQ]:"+str[2];
foot=1;//初始化索引
this->root->setNode(index,str[4]);
}
bool contains(string data){
return this->root->containsNode(data);
}
void remove(int index) throw (int){//remove的两种形式:输出根节点,删除子节点
if(index>counts|index<=0){//两个判断都要进行,所以不用短路或“||”
throw 0;//索引无效
return;
}
if(index==1){
this->root=this->root->next;
counts--;//链数减一
}else{
foot=1;
this->root->removeNode(this->root,index);
counts--;
}
}
void fileOut(char* path){//链表导出
if(this->isEmpty()){
cout<<"链表中没有数据"<<endl;
}
root->fileOutNode(path);
}
void fileIn(char* path)throw(int){//文件导入
ifstream file(path);
string data;
if(!file){
throw 0;
return;
}
getline(file,data);//整行导入
while(!file.eof()&&data!=""){//判断文件结尾
this->add(data);
getline(file,data);//整行导入
}
file.close();
}
void clean(){//清空根节点
this->root=NULL;
counts=0;
}
};
【2】MYGUI.h
#include<iostream>
#include<string>
#include<cstdlib>
using namespace std;
class MYGUI{//绘制界面工具类
public:
static void start(){//设置窗口大小
system("mode con cols=55 lines=36");
}
static void menu(){//画菜单
cout<<" +-----------------------------------------------------+\n";
cout<<" | |\n";
cout<<" | 0 000000 0 00000000 |\n";
cout<<" | 0 0 0 0 00000 0 |\n";
cout<<" | 00 0 0 0000000 |\n";
cout<<" | 0000000 0 0 0 |\n";
cout<<" | 00 0 0 0 000 0 0 00000000000 |\n";
cout<<" | 0 0000000 0 00000 0 |\n";
cout<<" | 0 0 0 0 0 0 0 0 0 0 |\n";
cout<<" | 0 0000000 0 0 0 0 000 0 |\n";
cout<<" | 0 0 0 0 0 0 0 0 0 0 |\n";
cout<<" | 0 0 0 00 00 0 0 0 0 0 0 |\n";
cout<<" | 0 0 0 00 00 0 00 |\n";
cout<<" | 0 000000000 0 0 00 |\n";
cout<<" | |\n";
cout<<" |-----------------------------------------------------|\n";
cout<<" | |\n";
cout<<" | [SoftMenu] |\n";
cout<<" | |\n";
cout<<" | (0) 通讯录链表的建立 |\n";
cout<<" | |\n";
cout<<" | (1) 通讯者链表的导入 |\n";
cout<<" | |\n";
cout<<" | (2) 通讯者结点的插入 |\n";
cout<<" | |\n";
cout<<" | (3) 通讯者结点的查询 |\n";
cout<<" | |\n";
cout<<" | (4) 通讯者结点的修改 |\n";
cout<<" | |\n";
cout<<" | (5) 通讯者结点的删除 |\n";
cout<<" | |\n";
cout<<" | (6) 通讯录链表的清空 |\n";
cout<<" | |\n";
cout<<" | (7) 通讯录链表的打印 |\n";
cout<<" | |\n";
cout<<" | (8) 通讯录链表的导出 |\n";
cout<<" | |\n";
cout<<" | (9) 退出管理系统 |\n";
cout<<" | |\n";
cout<<" | |\n";
cout<<" +-----------------------------------------------------+\n";
cout<<"【请输入功能选项】:"<<flush;//flush用于刷新此处的缓冲区,防止输入数据延滞
}
static void tip(string tip){//画提示信息
cout<<endl;
cout<<" +-----------------"<<tip<<"----------------+\n";
cout<<" | |\n";
}
static void tipMenu(){
cout<<" | |\n";
cout<<" +-----------------------------------------------------+\n";
cout<<" |(0)创建链表 (1)导入链表 (2)插入结点 (3)查询结点|\n";
cout<<" |(4)修改结点 (5)删除结点 (6)清空链表 (7)打印链表|\n";
cout<<" |(8)导出链表 (9)退出系统 |\n";
cout<<" +-----------------------------------------------------+\n";
cout<<"【请输入功能选项】:";
}
};
【3】通讯录.cpp
#include<iostream>
#include<string>
#include"MYGUI.h"//载入界面绘制类
#include"Link.h"//载入链表,提供数据存储支持
using namespace std;
int option;//输入选项
Link *plink=NULL;//链表指针
char input[2];//用户输入值,用于验证
class Util{//操作工具类
public:
//输入验证,确保数据为0-5整数,验证通过返回解码后的整数,否则返回-1
static int inputExam(char* input){
if(input[0]>='0'&&input[0]<='9'&&input[1]==NULL){
return input[0]-48;
}
return -1;
}
//输入选项编号,通过switch来匹配各个选项的功能
static void option(int option){
switch(option){
case 0:{ //创建链表
Util::creatLink();
break;
}
case 1:{//导入文件
Util::importLink();
break;
}
case 2:{//插入结点
Util::insertNode();
break;
}
case 3:{//查询结点
Util::findNode();
break;
}
case 4:{//修改节点
Util::alterNode();
break;
}
case 5:{//删除结点
Util::deleteNode();
break;
}
case 6:{//清空链表
Util::cleanLink();
break;
}
case 7:{//输出链表
Util::showLink();
break;
}
case 8:{//链表导出
Util::exportLink();
break;
}
}
}
static void creatLink(){
MYGUI::tip("(0) 通讯录链表的建立");
if(plink!=NULL){
char str;
cout<<"【已存在通讯录链表,是否重新创建(y/n)】:"<<flush;
cin>>str;
if(str=='y'){
plink->clean(); //并没有重新创建新的链表,只不过是将原有链表清空了,这样节约内存开支
cout<<"【创建通讯录链表成功】\n";
MYGUI::tipMenu();
}else if(str=='n'){
MYGUI::tipMenu();
}else{
cout<<"【输入错误,请输入(y/n)】\n";
Util::option(0);
}
} else{
plink=new Link();
cout<<"【创建通讯录链表成功】\n";
MYGUI::tipMenu();
}
}
static void importLink(){
MYGUI::tip("(1) 通讯录链表的导入");
if(plink!=NULL){
char path[100]; //支持char[100]范围内的路径
cout<<"【请输入导入文件路径名称(.txt)】:";
cin>>path;
try{
plink->fileIn(path);
cout<<"【导入文件成功】\n";
MYGUI::tipMenu();
}catch(int){
cout<<"【文件打开失败,检查输入路径】\n";
MYGUI::tipMenu();
}
}else{
cout<<"【尚未创建通讯者链表,请创建】\n";
MYGUI::tipMenu();
}
}
static void insertNode(){
MYGUI::tip("(2) 通讯者结点的插入");
if(plink!=NULL){
string str[4];//存储输入的数据
cout<<"【插入第"<<plink->count()+1<<"个通讯者信息】\n";
cout<<"通讯者 [姓名]:";
cin>>str[0];
cout<<"通讯者 [电话]:";
cin>>str[1];
cout<<"通讯者 [QQ]:";
cin>>str[2];
str[3]="[姓名]:"+str[0]+" [电话]:"+str[1]+" [QQ]:"+str[2];
plink->add(str[3]);
cout<<"【第"<<plink->count()<<"个通讯者信息录入完成】\n";
MYGUI::tipMenu();
}else{
cout<<"【尚未创建通讯者链表,请创建】\n";
MYGUI::tipMenu();
}
}
static void findNode(){
MYGUI::tip("(3) 通讯者结点的查询");
if(plink!=NULL){
if(plink->isEmpty()){
cout<<"【尚未录入通讯者信息,请录入】\n";
MYGUI::tipMenu();
}else{
int index;//输入的查询索引
string data;//存储查询得到的信息
cout<<"【请输入要查询通讯者的编号】:";
cin>>index;
try{//异常处理
data=plink->get(index);
cout<<"【查询结果】\n";
cout<<data<<endl;
MYGUI::tipMenu();
}catch(int){
cout<<"【查询失败,检查编号】\n";
MYGUI::tipMenu();
}
}
}else{
cout<<"【尚未创建通讯者链表,请创建】\n";
MYGUI::tipMenu();
}
}
static void alterNode(){
MYGUI::tip("(4) 通讯者结点的修改");
if(plink!=NULL){
if(plink->isEmpty()){
cout<<"【尚未录入通讯者信息,请录入】\n";
MYGUI::tipMenu();
}else{
int index;
cout<<"【请输入需要修改通讯者的编号】:";
cin>>index;
try{
plink->set(index);
cout<<"【修改成功】\n";
MYGUI::tipMenu();
}catch(int){
cout<<"【修改失败,检查编号】\n";
MYGUI::tipMenu();
}
}
}else{
cout<<"【尚未创建通讯者链表,请创建】\n";
MYGUI::tipMenu();
}
}
static void deleteNode(){
MYGUI::tip("(5) 通讯者结点的删除");
if(plink!=NULL){
if(plink->isEmpty()){
cout<<"【尚未录入通讯者信息,请录入】\n";
MYGUI::tipMenu();
}else{
int index;
cout<<"【请输入需要删除通讯者的编号】:";
cin>>index;
try{//异常处理
plink->remove(index);
cout<<"【删除成功】\n";
MYGUI::tipMenu();
}catch(int){
cout<<"【删除失败,检查编号】\n";
MYGUI::tipMenu();
}
}
}else{
cout<<"【尚未创建通讯者链表,请创建】\n";
MYGUI::tipMenu();
}
}
static void cleanLink(){
MYGUI::tip("(6) 通讯者链表的清空");
if(plink!=NULL){
if(plink->isEmpty()){
cout<<"【空链表,无需清空】\n";
MYGUI::tipMenu();
}else{
plink->clean();
cout<<"【清空成功】\n";
MYGUI::tipMenu();
}
} else{
cout<<"【未创建通讯者链表,请创建】\n";
MYGUI::tipMenu();
}
}
static void showLink(){
MYGUI::tip("(7) 通讯录链表的输出");
if(plink!=NULL){
if(plink->isEmpty()){
cout<<"【未录入通讯者信息,请录入】\n";
MYGUI::tipMenu();
}else{
cout<<"【输出结果】\n" ;
plink->toArray();
MYGUI::tipMenu();
}
}else{
cout<<"【尚未创建通讯者链表,请创建】\n";
MYGUI::tipMenu();
}
}
static void exportLink(){
MYGUI::tip("(8) 通讯录链表的导出");
if(plink!=NULL){
if(plink->isEmpty()){
cout<<"【尚未录入通讯者信息,请录入】\n";
MYGUI::tipMenu();
}else{
char path[100]; //支持char[100]范围内的路径
cout<<"【请输入导出文件路径名称(.txt)】:";
cin>>path;
try{
plink->fileOut(path);
cout<<"【导出文件成功】\n";
MYGUI::tipMenu();
}catch(int){
cout<<"【文件打开失败,检查输入路径】\n";
MYGUI::tipMenu();
}
}
}else{
cout<<"【尚未创建通讯者链表,请创建】\n";
MYGUI::tipMenu();
}
}
};
int main(){
MYGUI::menu();//画界面
cin>>input;
option=Util::inputExam(input);
while(option==-1){
cout<<"【输入错误,请输入 0~9 范围类的整数】:";
input[1]=NULL;//初始化input[1]
cin>>input;
option=Util::inputExam(input);
}
while(option!=9){//循环输入
Util::option(option);
cin>>input;
option=Util::inputExam(input);//输入验证
while(option==-1){
cout<<"【输入错误,请输入 0~9 范围类的整数】:";
input[1]=NULL;//初始化input[1]
cin>>input;
option=Util::inputExam(input);//输入验证
}
}
cout<<"\n【bye~】";//结束
return 0;
}
不足
没有统一的异常处理类,Link需要使用模板来增加链表存储数据类型,扩展链表用途。日后有时间再填。