【C++98 实战】图书管理系统的设计与实现(附完整源码)
在日常的软件开发学习中,控制台版的管理系统是夯实面向对象编程、数据持久化等核心知识点的经典案例。本文将基于C++98 标准,从零讲解一个具备角色权限控制、图书 / 用户管理、借阅追踪、逾期提醒、二进制持久化的图书管理系统,代码可直接编译运行,适合 C++ 入门学习者进阶练习。
注:本文使用AI写作
一、系统需求分析
核心功能
- 角色权限区分:管理员可添加 / 删除图书、管理用户;普通用户仅可查询、借阅、归还图书;
- 数据封装与管理:图书、用户信息封装为类,支持借阅状态追踪、逾期提醒(默认 30 天逾期);
- 数据持久化:通过二进制文件存储图书、用户数据,程序退出后数据不丢失;
- 兼容性:严格遵循 C++98 标准(无 C++11 的智能指针、nullptr 等特性),保证跨编译器兼容。
技术难点
- C++98 下的类序列化 / 反序列化(解决 string、多态对象的二进制存储问题);
- 基于多态的角色权限控制;
- 借阅时间计算与逾期逻辑实现;
- 动态内存管理(手动释放多态对象,避免内存泄漏)。
二、系统架构设计
1. 类结构设计
采用面向对象封装、继承、多态三大特性,核心类结构如下:
2. 核心模块划分
| 模块 | 功能描述 |
|---|---|
| 数据封装模块 | Book 类封装图书信息与借阅行为,User 基类 + 子类实现角色区分 |
| 持久化模块 | 二进制文件序列化 / 反序列化,实现图书、用户数据的持久化存储 |
| 权限控制模块 | 基于多态的菜单展示,区分管理员 / 普通用户操作权限 |
| 业务逻辑模块 | 图书借阅 / 归还、逾期检查、用户 / 图书增删改查等核心业务逻辑 |
| 交互模块 | 控制台菜单交互,接收用户输入并执行对应操作 |
三、核心技术实现
1. 数据封装:Book 类与 User 类
(1)Book 类:图书信息与行为封装
Book 类是核心数据载体,除了存储图书基本信息,还实现了借阅、归还、逾期检查等核心行为:
// 逾期检查核心逻辑
bool isOverdue() const {
if (!isBorrowed) return false;
time_t now = time(NULL);
double days = difftime(now, borrowTime) / (24 * 60 * 60); // 秒转天数
return days > OVERDUE_DAYS;
}
// 借阅操作:检查状态+记录借阅时间+绑定借阅人
bool borrowBook(const string& userId) {
if (isBorrowed) return false;
isBorrowed = true;
borrowTime = time(NULL);
borrowerId = userId;
return true;
}
(2)User 类:多态实现角色区分
User 作为基类封装通用属性,Admin 和 NormalUser 子类重写showMenu方法,实现不同角色的菜单展示:
// 基类虚函数
virtual void showMenu() const {
cout << "===== 普通用户菜单 =====" << endl;
cout << "1. 查询图书" << endl;
cout << "2. 借阅图书" << endl;
cout << "3. 归还图书" << endl;
cout << "0. 退出登录" << endl;
}
// 管理员子类重写
void showMenu() const override {
cout << "===== 管理员菜单 =====" << endl;
cout << "1. 添加图书" << endl;
cout << "2. 删除图书" << endl;
cout << "3. 添加用户" << endl;
cout << "4. 删除用户" << endl;
cout << "5. 查看所有图书" << endl;
cout << "6. 查看所有用户" << endl;
cout << "0. 退出登录" << endl;
}
2. 数据持久化:二进制文件序列化
C++98 中string无法直接写入二进制文件,需通过 “先写长度、再写内容” 的方式序列化;多态对象存储需先记录角色类型,再反序列化对应子类。
(1)Book 类序列化 / 反序列化
// 序列化:写入二进制文件
void save(ofstream& ofs) const {
// string序列化:长度+内容
int len = bookId.size();
ofs.write((char*)&len, sizeof(len));
ofs.write(bookId.c_str(), len);
len = bookName.size();
ofs.write((char*)&len, sizeof(len));
ofs.write(bookName.c_str(), len);
// 基础类型直接写入
ofs.write((char*)&isBorrowed, sizeof(isBorrowed));
ofs.write((char*)&borrowTime, sizeof(borrowTime));
}
// 反序列化:从二进制文件读取
void load(ifstream& ifs) {
int len;
ifs.read((char*)&len, sizeof(len));
bookId.resize(len);
ifs.read(&bookId[0], len);
ifs.read((char*)&len, sizeof(len));
bookName.resize(len);
ifs.read(&bookName[0], len);
ifs.read((char*)&isBorrowed, sizeof(isBorrowed));
ifs.read((char*)&borrowTime, sizeof(borrowTime));
}
(2)User 类多态序列化
存储时先写角色类型,读取时根据角色创建对应子类对象:
// 加载用户数据核心逻辑
void loadUsers() {
ifstream ifs(USER_FILE.c_str(), ios::binary);
if (!ifs) {
// 初始化默认管理员
userList.push_back(new Admin("admin", "管理员", "123456"));
saveUsers();
return;
}
int size;
ifs.read((char*)&size, sizeof(size));
for (int i = 0; i < size; ++i) {
int role;
ifs.read((char*)&role, sizeof(role)); // 先读角色
User* u = NULL;
if (role == 1) u = new Admin(); // 管理员
else u = new NormalUser(); // 普通用户
u->load(ifs);
userList.push_back(u);
}
ifs.close();
}
3. 业务逻辑:LibrarySystem 类
LibrarySystem 类是系统的 “中枢”,封装了所有核心业务逻辑,例如:
(1)图书借阅逻辑
bool borrowBook(const string& bookId, const string& userId) {
for (size_t i = 0; i < bookList.size(); ++i) {
if (bookList[i].getBookId() == bookId) {
bool res = bookList[i].borrowBook(userId);
if (res) {
saveBooks(); // 操作后立即保存
cout << "借阅成功!请在" << OVERDUE_DAYS << "天内归还。" << endl;
} else {
cout << "该图书已被借阅,无法借阅!" << endl;
}
return res;
}
}
cout << "未找到该图书!" << endl;
return false;
}
(2)用户登录逻辑
User* login(const string& userId, const string& pwd) {
for (size_t i = 0; i < userList.size(); ++i) {
if (userList[i]->getUserId() == userId && userList[i]->login(pwd)) {
cout << "登录成功!欢迎" << userList[i]->getUserName() << endl;
return userList[i];
}
}
cout << "用户ID或密码错误!" << endl;
return NULL;
}
4. 内存管理:避免内存泄漏
C++98 无智能指针,需手动释放vector<User*>中的动态对象,析构函数中完成内存释放 + 数据保存:
~LibrarySystem() {
saveBooks();
saveUsers();
// 释放用户列表内存
for (size_t i = 0; i < userList.size(); ++i) {
delete userList[i];
}
userList.clear();
}
四、系统使用说明
1. 环境要求
- 编译器:支持 C++98(GCC、Dev-C++、VS2008 及以上);
- 运行目录:程序运行后会自动生成
books.dat(图书数据)、users.dat(用户数据),请勿手动修改。
2. 初始账号
默认管理员账号:
- 用户 ID:admin
- 密码:123456
3. 核心操作示例
(1)管理员添加图书
登录→选择 “1. 添加图书”→输入图书编号、名称、作者→完成添加(自动去重校验)。
(2)普通用户借阅图书
登录→选择 “2. 借阅图书”→输入图书编号→系统检查状态并完成借阅(记录借阅时间)。
(3)图书逾期提醒
归还图书时,系统自动检查是否逾期,若逾期则提示 “该图书已逾期,请注意按时归还!”。
五、完整源码
#include <iostream>
#include <fstream>
#include <vector>
#include <string>
#include <ctime>
#include <algorithm>
#include <cstdlib>
#include <iomanip>
using namespace std;
// 常量定义
const string BOOK_FILE = "books.dat"; // 图书数据文件
const string USER_FILE = "users.dat"; // 用户数据文件
const int OVERDUE_DAYS = 30; // 借阅逾期天数阈值
// 图书类:封装图书信息与状态
class Book {
private:
string bookId; // 图书编号
string bookName; // 图书名称
string author; // 作者
bool isBorrowed; // 是否被借阅
time_t borrowTime;// 借阅时间(时间戳)
string borrowerId;// 借阅人编号
public:
// C++98默认构造函数(必须显式定义,否则序列化会出错)
Book() : isBorrowed(false), borrowTime(0) {}
Book(string id, string name, string auth)
: bookId(id), bookName(name), author(auth), isBorrowed(false), borrowTime(0) {}
// 序列化:写入二进制文件
void save(ofstream& ofs) const {
// 先写字符串长度,再写内容(解决string序列化问题)
int len = bookId.size();
ofs.write((char*)&len, sizeof(len));
ofs.write(bookId.c_str(), len);
len = bookName.size();
ofs.write((char*)&len, sizeof(len));
ofs.write(bookName.c_str(), len);
len = author.size();
ofs.write((char*)&len, sizeof(len));
ofs.write(author.c_str(), len);
ofs.write((char*)&isBorrowed, sizeof(isBorrowed));
ofs.write((char*)&borrowTime, sizeof(borrowTime));
len = borrowerId.size();
ofs.write((char*)&len, sizeof(len));
ofs.write(borrowerId.c_str(), len);
}
// 反序列化:从二进制文件读取
void load(ifstream& ifs) {
int len;
ifs.read((char*)&len, sizeof(len));
bookId.resize(len);
ifs.read(&bookId[0], len);
ifs.read((char*)&len, sizeof(len));
bookName.resize(len);
ifs.read(&bookName[0], len);
ifs.read((char*)&len, sizeof(len));
author.resize(len);
ifs.read(&author[0], len);
ifs.read((char*)&isBorrowed, sizeof(isBorrowed));
ifs.read((char*)&borrowTime, sizeof(borrowTime));
ifs.read((char*)&len, sizeof(len));
borrowerId.resize(len);
ifs.read(&borrowerId[0], len);
}
// 借阅操作
bool borrowBook(const string& userId) {
if (isBorrowed) return false;
isBorrowed = true;
borrowTime = time(NULL); // 记录当前时间戳
borrowerId = userId;
return true;
}
// 归还操作
bool returnBook() {
if (!isBorrowed) return false;
isBorrowed = false;
borrowerId = "";
borrowTime = 0;
return true;
}
// 检查是否逾期
bool isOverdue() const {
if (!isBorrowed) return false;
time_t now = time(NULL);
double days = difftime(now, borrowTime) / (24 * 60 * 60); // 转换为天数
return days > OVERDUE_DAYS;
}
// 获取器(C++98无nullptr,用空字符串/0表示空)
string getBookId() const { return bookId; }
string getBookName() const { return bookName; }
string getAuthor() const { return author; }
bool getIsBorrowed() const { return isBorrowed; }
string getBorrowerId() const { return borrowerId; }
time_t getBorrowTime() const { return borrowTime; }
// 显示图书信息
void showInfo() const {
cout << "图书编号:" << bookId << endl;
cout << "图书名称:" << bookName << endl;
cout << "作 者:" << author << endl;
cout << "状 态:" << (isBorrowed ? "已借阅" : "可借阅") << endl;
if (isBorrowed) {
cout << "借阅人ID:" << borrowerId << endl;
cout << "借阅时间:" << ctime(&borrowTime); // 转换为可读时间
if (isOverdue()) {
cout << "⚠️ 逾期提醒:已逾期" << (int)(difftime(time(NULL), borrowTime)/(24*60*60) - OVERDUE_DAYS) << "天!" << endl;
}
}
cout << "-------------------------" << endl;
}
};
// 用户基类:封装通用用户信息
class User {
protected:
string userId; // 用户编号
string userName; // 用户名
string password; // 密码
int role; // 角色:0-普通用户,1-管理员
public:
User() : role(0) {}
User(string id, string name, string pwd, int r)
: userId(id), userName(name), password(pwd), role(r) {}
// 序列化
void save(ofstream& ofs) const {
int len = userId.size();
ofs.write((char*)&len, sizeof(len));
ofs.write(userId.c_str(), len);
len = userName.size();
ofs.write((char*)&len, sizeof(len));
ofs.write(userName.c_str(), len);
len = password.size();
ofs.write((char*)&len, sizeof(len));
ofs.write(password.c_str(), len);
ofs.write((char*)&role, sizeof(role));
}
// 反序列化
void load(ifstream& ifs) {
int len;
ifs.read((char*)&len, sizeof(len));
userId.resize(len);
ifs.read(&userId[0], len);
ifs.read((char*)&len, sizeof(len));
userName.resize(len);
ifs.read(&userName[0], len);
ifs.read((char*)&len, sizeof(len));
password.resize(len);
ifs.read(&password[0], len);
ifs.read((char*)&role, sizeof(role));
}
// 登录验证
bool login(const string& pwd) const {
return password == pwd;
}
// 获取器
string getUserId() const { return userId; }
string getUserName() const { return userName; }
int getRole() const { return role; }
// 虚函数:用户操作菜单(子类重写)
virtual void showMenu() const {
cout << "===== 普通用户菜单 =====" << endl;
cout << "1. 查询图书" << endl;
cout << "2. 借阅图书" << endl;
cout << "3. 归还图书" << endl;
cout << "0. 退出登录" << endl;
cout << "========================" << endl;
}
virtual ~User() {} // 虚析构函数,保证子类析构正常
};
// 管理员子类:扩展管理员权限
class Admin : public User {
public:
Admin() : User() {}
Admin(string id, string name, string pwd)
: User(id, name, pwd, 1) {}
// 重写菜单
void showMenu() const {
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;
}
};
// 普通用户子类
class NormalUser : public User {
public:
NormalUser() : User() {}
NormalUser(string id, string name, string pwd)
: User(id, name, pwd, 0) {}
};
// 数据管理类:封装持久化与核心操作
class LibrarySystem {
private:
vector<Book> bookList; // 图书列表
vector<User*> userList; // 用户列表(多态存储)
// 加载图书数据
void loadBooks() {
ifstream ifs(BOOK_FILE.c_str(), ios::binary);
if (!ifs) return; // 文件不存在则跳过
int size;
ifs.read((char*)&size, sizeof(size)); // 先读列表长度
bookList.resize(size);
for (int i = 0; i < size; ++i) {
bookList[i].load(ifs);
}
ifs.close();
}
// 保存图书数据
void saveBooks() {
ofstream ofs(BOOK_FILE.c_str(), ios::binary | ios::trunc);
int size = bookList.size();
ofs.write((char*)&size, sizeof(size)); // 先写列表长度
for (size_t i = 0; i < bookList.size(); ++i) {
bookList[i].save(ofs);
}
ofs.close();
}
// 加载用户数据
void loadUsers() {
ifstream ifs(USER_FILE.c_str(), ios::binary);
if (!ifs) {
// 初始化默认管理员
userList.push_back(new Admin("admin", "管理员", "123456"));
saveUsers();
return;
}
int size;
ifs.read((char*)&size, sizeof(size));
for (int i = 0; i < size; ++i) {
int role;
ifs.read((char*)&role, sizeof(role)); // 先读角色,区分类型
User* u = NULL;
if (role == 1) {
u = new Admin();
} else {
u = new NormalUser();
}
u->load(ifs);
userList.push_back(u);
}
ifs.close();
}
// 保存用户数据
void saveUsers() {
ofstream ofs(USER_FILE.c_str(), ios::binary | ios::trunc);
int size = userList.size();
ofs.write((char*)&size, sizeof(size));
for (size_t i = 0; i < userList.size(); ++i) {
int role = userList[i]->getRole();
ofs.write((char*)&role, sizeof(role)); // 先写角色
userList[i]->save(ofs);
}
ofs.close();
}
public:
// 构造函数:加载数据
LibrarySystem() {
loadBooks();
loadUsers();
}
// 析构函数:释放内存+保存数据
~LibrarySystem() {
saveBooks();
saveUsers();
// 释放用户列表内存(C++98无智能指针,手动释放)
for (size_t i = 0; i < userList.size(); ++i) {
delete userList[i];
}
userList.clear();
}
// 用户登录
User* login(const string& userId, const string& pwd) {
for (size_t i = 0; i < userList.size(); ++i) {
if (userList[i]->getUserId() == userId && userList[i]->login(pwd)) {
cout << "登录成功!欢迎" << userList[i]->getUserName() << endl;
return userList[i];
}
}
cout << "用户ID或密码错误!" << endl;
return NULL;
}
// ------------------------ 管理员操作 ------------------------
// 添加图书
void addBook(const string& id, const string& name, const string& author) {
// 检查编号是否重复
for (size_t i = 0; i < bookList.size(); ++i) {
if (bookList[i].getBookId() == id) {
cout << "图书编号已存在!" << endl;
return;
}
}
bookList.push_back(Book(id, name, author));
saveBooks();
cout << "图书添加成功!" << endl;
}
// 删除图书
void deleteBook(const string& bookId) {
for (vector<Book>::iterator it = bookList.begin(); it != bookList.end(); ++it) {
if (it->getBookId() == bookId) {
if (it->getIsBorrowed()) {
cout << "该图书已被借阅,无法删除!" << endl;
return;
}
bookList.erase(it);
saveBooks();
cout << "图书删除成功!" << endl;
return;
}
}
cout << "未找到该图书!" << endl;
}
// 添加用户
void addUser(const string& id, const string& name, const string& pwd, int role) {
// 检查编号是否重复
for (size_t i = 0; i < userList.size(); ++i) {
if (userList[i]->getUserId() == id) {
cout << "用户ID已存在!" << endl;
return;
}
}
if (role == 1) {
userList.push_back(new Admin(id, name, pwd));
} else {
userList.push_back(new NormalUser(id, name, pwd));
}
saveUsers();
cout << "用户添加成功!" << endl;
}
// 删除用户
void deleteUser(const string& userId) {
for (vector<User*>::iterator it = userList.begin(); it != userList.end(); ++it) {
if ((*it)->getUserId() == userId) {
// 检查是否是默认管理员
if ((*it)->getUserId() == "admin") {
cout << "禁止删除默认管理员!" << endl;
return;
}
delete *it; // 释放内存
userList.erase(it);
saveUsers();
cout << "用户删除成功!" << endl;
return;
}
}
cout << "未找到该用户!" << endl;
}
// 查看所有图书
void showAllBooks() const {
if (bookList.empty()) {
cout << "暂无图书!" << endl;
return;
}
for (size_t i = 0; i < bookList.size(); ++i) {
bookList[i].showInfo();
}
}
// 查看所有用户
void showAllUsers() const {
if (userList.empty()) {
cout << "暂无用户!" << endl;
return;
}
for (size_t i = 0; i < userList.size(); ++i) {
cout << "用户ID:" << userList[i]->getUserId() << endl;
cout << "用户名:" << userList[i]->getUserName() << endl;
cout << "角 色:" << (userList[i]->getRole() == 1 ? "管理员" : "普通用户") << endl;
cout << "-------------------------" << endl;
}
}
// ------------------------ 普通用户操作 ------------------------
// 查询图书(按名称/编号)
void searchBook(const string& keyword) const {
bool found = false;
for (size_t i = 0; i < bookList.size(); ++i) {
if (bookList[i].getBookId() == keyword || bookList[i].getBookName().find(keyword) != string::npos) {
bookList[i].showInfo();
found = true;
}
}
if (!found) {
cout << "未找到相关图书!" << endl;
}
}
// 借阅图书
bool borrowBook(const string& bookId, const string& userId) {
for (size_t i = 0; i < bookList.size(); ++i) {
if (bookList[i].getBookId() == bookId) {
bool res = bookList[i].borrowBook(userId);
if (res) {
saveBooks();
cout << "借阅成功!请在" << OVERDUE_DAYS << "天内归还。" << endl;
} else {
cout << "该图书已被借阅,无法借阅!" << endl;
}
return res;
}
}
cout << "未找到该图书!" << endl;
return false;
}
// 归还图书
bool returnBook(const string& bookId) {
for (size_t i = 0; i < bookList.size(); ++i) {
if (bookList[i].getBookId() == bookId) {
bool res = bookList[i].returnBook();
if (res) {
if (bookList[i].isOverdue()) {
cout << "⚠️ 该图书已逾期,请注意按时归还!" << endl;
}
saveBooks();
cout << "归还成功!" << endl;
} else {
cout << "该图书未被借阅,无需归还!" << endl;
}
return res;
}
}
cout << "未找到该图书!" << endl;
return false;
}
};
// 主函数:交互逻辑
int main() {
LibrarySystem libSys;
User* currentUser = NULL;
while (true) {
if (currentUser == NULL) {
// 登录界面
cout << "===== 图书管理系统 =====" << endl;
cout << "1. 登录" << endl;
cout << "0. 退出系统" << endl;
cout << "========================" << endl;
cout << "请选择操作:";
int choice;
cin >> choice;
if (choice == 1) {
string userId, pwd;
cout << "请输入用户ID:";
cin >> userId;
cout << "请输入密码:";
cin >> pwd;
currentUser = libSys.login(userId, pwd);
} else if (choice == 0) {
cout << "感谢使用,再见!" << endl;
break;
} else {
cout << "输入错误,请重新选择!" << endl;
}
} else {
// 角色菜单
currentUser->showMenu();
cout << "请选择操作:";
int choice;
cin >> choice;
if (currentUser->getRole() == 1) {
// 管理员操作
switch (choice) {
case 1: { // 添加图书
string id, name, author;
cout << "请输入图书编号:";
cin >> id;
cin.ignore(); // 忽略换行符
cout << "请输入图书名称:";
getline(cin, name);
cout << "请输入作者:";
getline(cin, author);
libSys.addBook(id, name, author);
break;
}
case 2: { // 删除图书
string id;
cout << "请输入要删除的图书编号:";
cin >> id;
libSys.deleteBook(id);
break;
}
case 3: { // 添加用户
string id, name, pwd;
int role;
cout << "请输入用户ID:";
cin >> id;
cin.ignore();
cout << "请输入用户名:";
getline(cin, name);
cout << "请输入密码:";
cin >> pwd;
cout << "请输入角色(0-普通用户,1-管理员):";
cin >> role;
libSys.addUser(id, name, pwd, role);
break;
}
case 4: { // 删除用户
string id;
cout << "请输入要删除的用户ID:";
cin >> id;
libSys.deleteUser(id);
break;
}
case 5: // 查看所有图书
libSys.showAllBooks();
break;
case 6: // 查看所有用户
libSys.showAllUsers();
break;
case 0: // 退出登录
currentUser = NULL;
cout << "已退出登录!" << endl;
break;
default:
cout << "输入错误,请重新选择!" << endl;
}
} else {
// 普通用户操作
switch (choice) {
case 1: { // 查询图书
string keyword;
cin.ignore();
cout << "请输入图书编号/名称关键词:";
getline(cin, keyword);
libSys.searchBook(keyword);
break;
}
case 2: { // 借阅图书
string bookId;
cout << "请输入要借阅的图书编号:";
cin >> bookId;
libSys.borrowBook(bookId, currentUser->getUserId());
break;
}
case 3: { // 归还图书
string bookId;
cout << "请输入要归还的图书编号:";
cin >> bookId;
libSys.returnBook(bookId);
break;
}
case 0: // 退出登录
currentUser = NULL;
cout << "已退出登录!" << endl;
break;
default:
cout << "输入错误,请重新选择!" << endl;
}
}
}
}
return 0;
}
六、扩展与优化建议
- 功能扩展:添加图书分类、借阅历史查询、密码修改、逾期罚款计算等功能;
- 性能优化:对图书 / 用户列表的查询操作添加哈希索引,提升查询效率;
- 界面优化:结合 QT 等库实现图形化界面,提升用户体验;
- 安全优化:对用户密码进行加密存储(如 MD5),避免明文泄露。
七、总结
本系统基于 C++98 标准,完整实现了图书管理系统的核心功能,重点体现了面向对象的封装、继承、多态思想,以及二进制文件持久化、动态内存管理等 C++ 核心知识点。代码结构清晰、注释完善,适合 C++ 入门学习者理解和扩展,也可作为课程设计、毕业设计的参考案例。
如果本文对你有帮助,欢迎点赞、收藏、评论,也欢迎指出代码中的不足和优化建议!
更多推荐

所有评论(0)