一、来源:软件工程2班李鹏飞去年的大作业`
点击查看代码
#include <iostream>
#include <string>
#include <fstream>
using namespace std;//定义客户类型
enum eGuestType // 在高版本VS中,需要用enum class,在低版本的vs中,直接用enum也可以
{e_member,e_honour,e_layfolk
};class buyer
{
protected:string name;int buyerId;string address;double pay;eGuestType type; //添加这个字段
public:buyer();buyer(string n, int b, string a,double p);string getbuyname();int getid();string getaddress();double getpay();virtual void setpay(double p= 0) = 0;eGuestType gettype();//添加这个函数
};eGuestType buyer::gettype()
{return type;
}class member :public buyer
{int leagure_grad;
public:member(string n, int b, int l, string a, double p) :buyer(n, b, a, p){leagure_grad = l;type = e_member; //添加这个设置}void setleaguregrad(int l) { leagure_grad = l; }int getleaguregrad() { return leagure_grad; }void display();void setpay(double p);
};class honoured_guest :public buyer
{double discount_rate;
public:honoured_guest(string n, int b, double r, string a, double p) :buyer(n, b, a, p){discount_rate = r;type = e_honour;//添加这个设置}void setrate(double r) { discount_rate = r; }double getrate() { return discount_rate; }void display();void setpay(double p);
};class layfolk :public buyer
{
public:layfolk(string n, int b, string a, double p) :buyer(n, b, a, p){type = e_layfolk;}void display();void setpay(double p);
};buyer::buyer()
{name = " ";buyerId = 0;address = " ";pay = 0;type = e_layfolk;//添加这个设置
}
buyer::buyer(string n, int b, string a, double p)
{name = n;buyerId = b;address = a;pay = p;type = e_layfolk;//添加这个设置
}double buyer::getpay()
{return pay;
}
string buyer::getaddress()
{return address;
}
string buyer::getbuyname()
{return name;
}
int buyer::getid()
{return buyerId;
}void member::display()
{cout << "购书人姓名:" << name << endl;cout << "购书人编号:" << buyerId << endl;cout << "购书人地址:" << address << endl;cout << "购书人为会员,级别:" << leagure_grad << endl;
}void member::setpay(double p)
{if (leagure_grad == 1)pay = 0.95 * p + pay;else if (leagure_grad == 2)pay = 0.9 * p + pay;else if (leagure_grad == 3)pay = 0.85 * p + pay;else if (leagure_grad == 4)pay = 0.8 * p + pay;else if (leagure_grad == 5)pay = 0.7 * p + pay;elsecout << "级别错误!";
}void honoured_guest::display()
{cout << "购书人姓名:" << name << endl;cout << "购书人编号:" << buyerId << endl;cout << "购书人地址:" << address << endl;cout << "购书人为贵宾!折扣率为:" << discount_rate << endl;
}void honoured_guest::setpay(double p)
{pay = pay + (1 - discount_rate) * p;
}void layfolk::display()
{cout << "购书人姓名:" << name << endl;cout << "购书人编号:" << buyerId << endl;cout << "购书人地址:" << address << endl;cout << "购书人为普通人" << endl;
}void layfolk::setpay(double p)
{pay = pay + p;
}class book
{
protected:string book_ID;string book_name;string auther;string publishing;double price;
public:book();book(string b_id, string b_n, string au, string pu, double pr);void display();string getbook_ID();string getbook_name();string getauther();string getpublishing();double getprice();
};book::book()
{book_ID = " ";book_name = " ";auther = " ";publishing = " ";price = 0;
}
book::book(string b_id, string b_n, string au, string pu, double pr)
{book_ID = b_id;book_name = b_n;auther = au;publishing = pu;price = pr;
}string book::getbook_ID()
{return book_ID;
}
string book::getbook_name()
{return book_name;
}
string book::getauther()
{return auther;
}string book::getpublishing()
{return publishing;
}
double book::getprice()
{return price;
}void book::display()
{cout << "书号:" << book_ID << endl;cout << "书名:" << book_name << endl;cout << "作者:" << auther << endl;cout << "出版社:" << publishing << endl;cout << "定价:" << price << endl;
}//定义订单类,这里规定用户ID和书籍ID都是唯一的,根据用户ID可以从用户信息数组中查找到完整的用户信息,书籍类似
struct Dingdan
{int buyerid;//用户idint count; //购买数量string bookid[20]; //保存购买的书籍id
};//从文件中读取用户信息
void GetUserInfo(buyer* gu[], int& nmb_gu)
{//1.读取用户信息ifstream ifsb;ifsb.open("guest.txt", ios::in);if (!ifsb.is_open()){cout << "用户文件打开失败" << endl;return ;}while (!ifsb.eof()){int t = 0, level = 0, id = 0;double rate = 0, pay = 0;string name = "", ad = "";ifsb >> t; //读取类型if (t == 1) //读取会员书籍{ifsb >> id >> name >> ad >> pay >> level;if (id > 0) //验证读取成功{gu[nmb_gu] = new member(name, id, level, ad, pay);nmb_gu++;}}else if (t == 2) //读取贵宾书籍{ifsb >> id >> name >> ad >> pay >> rate;if (id > 0){gu[nmb_gu] = new honoured_guest(name, id, rate, ad, pay);nmb_gu++;}}else if (t == 3){ifsb >> id >> name >> ad >> pay;if (id > 0){gu[nmb_gu] = new layfolk(name, id, ad, pay);nmb_gu++;}}}ifsb.close();}
//从文件中读取书籍信息
void GetBookInfo(book* bs[], int& nmb_bs)
{//2.读取书籍信息ifstream ifsk;ifsk.open("book.txt", ios::in);if (!ifsk.is_open()){cout << "用户文件打开失败" << endl;return ;}while (!ifsk.eof()){string id = "", name = "", au = "", pub = "";double price = 0;ifsk >> id >> name >> au >> pub >> price;if (!id.empty()){bs[nmb_bs] = new book(id, name, au, pub, price);nmb_bs++;}}ifsk.close();}
//更新用户信息文件
void UpdateUser(buyer* gu[], int nmb_gu)
{//将用户信息写入文件ofstream ofsb;ofsb.open("guest.txt", ios::out);for (int i = 0; i < nmb_gu; i++){if (gu[i]->gettype() == e_member){member* b1 = (member*)gu[i];//写入会员数据ofsb << "1 " << b1->getid() << " " << b1->getbuyname() << " " << b1->getaddress() << " " << b1->getpay() << " " << b1->getleaguregrad() << endl;}else if (gu[i]->gettype() == e_honour){//写入贵宾honoured_guest* b2 = (honoured_guest*)gu[i];ofsb << "2 " << b2->getid() << " " << b2->getbuyname() << " " << b2->getaddress() << " " << b2->getpay() << " " << b2->getrate() << endl;}else{//写入普通人layfolk* b3 = (layfolk*)gu[i];ofsb << "3 " << b3->getid() << " " << b3->getbuyname() << " " << b3->getaddress() << " " << b3->getpay() << endl;}}ofsb.close();cout << "用户信息保存成功!" << endl;
}
//更新书籍信息文件
void UpdateBooks(book* bs[], int nmb_bs)
{//将书籍信息写入文件ofstream ofsk;ofsk.open("book.txt", ios::out);for(int i=0;i<nmb_bs;i++)ofsk << bs[i]->getbook_ID() << " " << bs[i]->getbook_name() << " " << bs[i]->getauther() << " " << bs[i]->getpublishing() << " " << bs[i]->getprice() << endl;ofsk.close();cout << "书籍信息保存成功!" << endl;
}//显示当前所有用户信息
void showAllGuest(buyer* gu[], int nmb_gu)
{if (nmb_gu == 0){cout << "用户文件为空" << endl;return;}cout << "读取的用户信息为:" << endl;//显示读取的用户信息for (int i = 0; i < nmb_gu; i++){if (gu[i]->gettype() == e_member){member* p = (member*)gu[i];p->display();}else if (gu[i]->gettype() == e_honour){honoured_guest* p = (honoured_guest*)gu[i];p->display();}else if (gu[i]->gettype() == e_layfolk){layfolk* p = (layfolk*)gu[i];p->display();}}
}//显示所有书籍信心
void showAllBooks(book* bs[], int nmb_bs)
{if (nmb_bs == 0){cout << "目前暂无书籍信息" << endl;return;}//显示书籍信息cout << "书籍信息:" << endl;for (int i = 0; i < nmb_bs; i++)bs[i]->display();
}//判断用户ID是否已经存在
int isIdExist(buyer* gu[], int nmb, int id)
{for (int i = 0; i < nmb; i++){if (gu[i]->getid() == id)return 1;}return 0;
}//新增用户
void AddUser(buyer* gu[], int &nmb_gu)
{int id,level,type;string name, ad;double pay, rate;cout << "请输入用户id:";//这里判断一下ID是否已经存在while (1){cin >> id;if (isIdExist(gu, nmb_gu, id))cout << "该ID已存在,请重新输入:";elsebreak;}cout << "请输入姓名:";cin >> name;cout << "请输入地址:";cin >> ad;cout << "请输入支付金额: ";cin >> pay;while (1){cout << "请选择用户类型:1.会员 2.贵宾 3.普通用户 :";cin >> type;if (type >= 1 && type <= 3)break;elsecout << "输入错误,";}if (type == 1){cout << "请输入会员等级:";cin >> level;member* b = new member(name, id, level, ad, pay);gu[nmb_gu] = b;nmb_gu++;}else if (type == 2){cout << "请输入折扣率:";cin >> rate;honoured_guest* g = new honoured_guest(name, id, rate, ad, pay);gu[nmb_gu] = g;nmb_gu++;}else{layfolk* ll = new layfolk(name, id, ad, pay);gu[nmb_gu] = ll;nmb_gu++;}cout << "添加成功!" << endl;
}//判断书籍ID是否已经存在
int isBookIdExist(book* bs[], int nmb, string id)
{for (int i = 0; i < nmb; i++){if (bs[i]->getbook_ID().compare(id) == 0)return 1;}return 0;
}//新增书籍
void AddBook(book* bs[], int &nmb_bs)
{string id, name, au, pub;double price;cout << "请输入书籍编号:";while (1){cin >> id;if (isBookIdExist(bs, nmb_bs, id))cout << "该编号已经存在,请重新输入:";elsebreak;}cout << "请输入书籍名称:";cin >> name;cout << "请输入作者:";cin >> au;cout << "请输入出版社:";cin >> pub;cout << "请输入定价:";cin >> price;book* b = new book(id, name, au, pub, price);bs[nmb_bs++] = b;cout << "书籍添加成功!" << endl;
}//购买书籍
void shopping(buyer* gu[], int nmb_gu, book* bs[], int nmb_bs,Dingdan dd[],int &nmb_dd)
{int userid,count = 0;string bookid;cout << "请输入用户id:";while (1){cin >> userid;//判断id是否已经存在if (isIdExist(gu, nmb_gu, userid))break;elsecout << "该用户ID不存在,请重新输入:";}cout << "请输入购买的书籍数量:";cin >> count;dd[nmb_dd].buyerid = userid;dd[nmb_dd].count = count;for (int i = 0; i < count; i++){cout << "请输入书籍ID:";while (1){cin >> bookid;if (isBookIdExist(bs, nmb_bs, bookid))break;elsecout << "该书籍不存在,请重新输入:";}dd[nmb_dd].bookid[i] = bookid;}nmb_dd++;//订单写入文件ofstream ofsdd;ofsdd.open("dingdan.txt", ios::out);for (int i = 0; i < 2; i++){//写入用户id和书籍数量ofsdd << dd[i].buyerid << " " << dd[i].count << " ";//写入书籍idfor (int j = 0; j < dd[i].count; j++){if (j < dd[i].count - 1)ofsdd << dd[i].bookid[j] << " ";elseofsdd << dd[i].bookid[j] << endl;}}ofsdd.close(); //写入完毕cout << "订单信息已写入文件!" << endl;
}//显示订单信息
void showDd(Dingdan dd[], int nmb_dd)
{cout << "订单信息:" << endl;cout << "----------------------------------------" << endl;for (int i = 0; i < nmb_dd; i++){cout << "顾客ID:" << dd[i].buyerid << endl;for (int j = 0; j < dd[i].count; j++)cout << " " << dd[i].bookid[j] << endl;cout << "----------------------------------------" << endl;}
}int main()
{int op;book* bs[50]; //存储书籍int nmb_bs = 0;//书籍数量buyer* gu[50]; //存储用户int nmb_gu = 0; //用户数量Dingdan dd[50]; //保存订单信息int nmb_dd = 0; //订单数量int flag = 1;//程序开始时,从文件读取已有信息GetUserInfo(gu, nmb_gu);GetBookInfo(bs, nmb_bs);while (flag){system("cls");cout << "1.增加顾客" << endl;cout << "2.查看所有顾客信息" << endl;cout << "3.添加书籍" << endl;cout << "4.查看所有书籍信息" << endl;cout << "5.新增订单" << endl;cout << "6.显示所有订单" << endl;cout << "0.退出系统" << endl;cout << "请选择:";cin >> op;switch (op){case 1: AddUser(gu,nmb_gu); break;case 2: showAllGuest(gu,nmb_gu); break;case 3: AddBook(bs,nmb_bs); break;case 4: showAllBooks(bs,nmb_bs); break;case 5: shopping(gu,nmb_gu,bs,nmb_bs,dd,nmb_dd); break;case 6: showDd(dd, nmb_dd); break;case 0:flag = 0; break;}system("pause");}return 0;
}
CPU:AMD Ryzen 7 7840H w/ Radeon 780M Graphics 3.80 GHz
RAM:16GB
磁盘:NVMe Micron_3400_MTFDKBA512TFH 容量:477 GB
操作系统:Windows 11
IDE:Microsoft Visual Studio Community 2022 (64 位) - Current 版本 17.12.3
三、项目介绍:
这段代码实现了一个简单的书店管理系统,包含顾客管理、书籍管理和订单管理功能。
1.增加顾客:添加新增顾客ID,姓名,地址,用户类型,支付金额,会员等级。
2.增加书籍:添加新增书籍编号,名称,作者,出版社以及定价。
3.新增订单:新增订单,输入用户ID,购买书籍。
四、主要问题
内存泄漏:
问题:动态分配的 buyer 和 book 对象未释放。
解决:在程序退出前释放内存。
for (int i = 0; i < nmb_gu; ++i) delete gu[i];
for (int i = 0; i < nmb_bs; ++i) delete bs[i];
订单保存错误:
问题:订单写入使用错误循环且覆盖模式错误。
解决:使用正确循环并改为追加模式。
ofsdd.open("dingdan.txt", ios::app);
for (int i = 0; i < nmb_dd; i++) { /* 写入所有订单 / }
支付未更新:
问题:订单未计算金额并更新用户支付。
解决:计算总价后调用 setpay。
buyer currentBuyer = /* 查找用户 /;
double totalPrice = / 计算书籍总价 /;
currentBuyer->setpay(totalPrice);
退出未保存:
问题:修改的数据退出时未保存。
解决:退出前调用保存函数。
case 0:
UpdateUser(gu, nmb_gu);
UpdateBooks(bs, nmb_bs);
flag = 0;
break;
数组越界:
问题:未检查数组边界。
解决:添加数组容量检查。
if (nmb_gu >= 50) { / 报错并返回 / }
文件读取问题:
问题:使用 eof() 导致重复读取。
解决:改用读取操作作为循环条件。
while (ifsb >> t) { / 处理读取 */ }
五、总结
该代码实现了一个基于C++的书店管理系统,包含顾客信息管理、书籍管理和订单管理三大核心功能。系统通过面向对象设计,构建了buyer基类及其派生类(会员、贵宾、普通用户),利用多态特性实现差异化的价格计算逻辑。book类管理书籍基础信息,Dingdan结构体记录订单数据,体现了基本的业务建模能力。程序通过文件读写实现数据持久化(guest.txt、book.txt、dingdan.txt),并采用菜单驱动模式提供用户交互界面。
主要优点:
继承与多态应用合理,通过虚函数setpay()实现不同用户类型的差异化价格计算逻辑。
文件操作模块化,分离了数据读取(GetUserInfo/GetBookInfo)和保存(UpdateUser/UpdateBooks)功能。
输入验证机制初步完善,如检测用户ID和书籍ID的唯一性。
突出问题:
内存泄漏风险:动态创建的buyer和book对象未在程序退出前释放。
订单功能缺陷:订单记录仅保存书籍ID而未计算实际金额,用户支付总额未更新。
文件写入错误:订单保存时错误使用固定循环(for(int i=0;i<2;i++)),导致数据覆盖或不完整。
数据边界风险:使用静态数组(如bookid[20])存在越界风险,未实现动态扩展。
健壮性不足:未处理文件打开失败、输入数据类型错误等异常场景。
改进方向:
引入智能指针或vector容器替代原始指针和静态数组,提升内存安全性。重构订单处理逻辑,将书籍ID与购买数量关联,并实现自动金额计算。完善异常处理机制,增加try-catch块和输入有效性校验。使用追加模式(ios::app)保存订单避免数据覆盖。增加数据统计功能(如用户消费排行、书籍销量统计)以增强实用性。总体而言,该代码完成了基础功能框架,体现了面向对象的核心思想,但在工程实践层面存在显著缺陷,需通过内存管理优化、异常处理增强和业务逻辑完善来提升系统的健壮性和实用性。