C++课设:从零开始打造影院订票系统
名人说:路漫漫其修远兮,吾将上下而求索。—— 屈原《离骚》
创作者:Code_流苏(CSDN)(一个喜欢古诗词和编程的Coder😊)
专栏介绍:《编程项目实战》
目录
- 一、项目背景与需求分析
- 二、系统架构设计与技术选型
- 1. 三层架构设计
- 2. 核心技术选型
- 3. 数据结构设计
- 三、核心类设计详解
- 1. 电影信息类 (`Movie`)
- 2. 影厅管理类 (`Cinema`)
- 3. 座位状态管理
- 4. 票务管理类 (`Ticket`)
- 四、关键功能实现
- 1. 智能选座算法
- 2. 多票种优惠算法
- 3. 票据生成与管理
- 4. 退票机制实现
- 五、完整代码展示
- 六、总结与扩展建议
- 1. 项目成果总结
- 2. 性能优化建议
- 3. 功能扩展方向
- 4. 学习价值与应用场景
今天,将介绍如何使用C++开发一个功能完整的影院订票系统,包含电影排片展示、用户选座、票价结算、退票、多影厅管理等核心功能。代码完全兼容DevC++ 5.11,适合初学者学习和课程设计使用。
一、项目背景与需求分析
随着数字化时代的到来,传统的人工售票方式已经无法满足现代观众的需求。电影选座与订票系统的研究对于推动电影产业的数字化进程具有重要意义,一个高效便民的订票系统不仅能提升用户体验,还能为影院运营提供数据支持。
我们的影院订票系统需要满足以下核心功能需求:
- 🎬 电影信息管理:展示电影详情、时长、类型、票价等信息
- 📅 排片管理:支持多影厅、多场次的排片安排
- 💺 智能选座:提供可视化座位图,支持多座位选择
- 💰 票价结算:支持成人票、学生票、老人票等不同优惠策略
- 🎫 票务管理:生成电子票据,支持退票功能
- 🏢 多影厅支持:管理不同规格的影厅布局
二、系统架构设计与技术选型
1. 三层架构设计
我们采用经典的三层架构模式,确保代码结构清晰、职责分离:
架构说明:
- 用户交互层:负责菜单展示、用户输入处理、界面显示
- 业务逻辑层:核心功能实现,包括选座逻辑、票价计算、数据验证
- 数据存储层:使用STL容器管理电影、影厅、场次、票据等数据
2. 核心技术选型
技术组件 | 选择方案 | 优势分析 |
---|---|---|
容器管理 | vector<> | 动态数组,操作简单,内存连续 |
字符串处理 | string | 相比C字符串更安全便捷 |
枚举类型 | enum | 提高代码可读性和类型安全 |
输入输出 | iostream | 类型安全的输入输出流 |
格式化输出 | iomanip | 精确控制输出格式 |
3. 数据结构设计
核心数据实体关系:
Movie(电影) ←→ ShowTime(场次) ←→ Cinema(影厅) ←→ Ticket(票据)↑ ↑ ↑ ↑基础信息 排片信息 座位管理 交易记录
三、核心类设计详解
1. 电影信息类 (Movie
)
设计思路:存储电影的基本属性,作为排片的基础数据。
class Movie {
public:string name; // 电影名称string director; // 导演int duration; // 时长(分钟)string genre; // 类型double basePrice; // 基础票价// 构造函数支持快速初始化Movie(const string& n, const string& d, int dur, const string& g, double price);
};
设计亮点:
- 使用
const string&
参数避免不必要的字符串拷贝 basePrice
为基础价格,便于后续优惠计算
2. 影厅管理类 (Cinema
)
设计思路:管理影厅布局和座位状态,提供座位操作的核心接口。
class Cinema {
private:vector<vector<SeatStatus> > seats; // 二维座位矩阵public:int cinemaId; // 影厅编号string name; // 影厅名称int rows, cols; // 行数和列数void displaySeats() const; // 显示座位图bool isSeatAvailable(int row, int col) const; // 检查座位可用性void selectSeat(int row, int col); // 选择座位void occupySeat(int row, int col); // 占用座位void releaseSeat(int row, int col); // 释放座位
};
技术细节:
- 使用二维vector模拟真实影厅布局
- 座位状态使用枚举类型提高代码可读性
- 提供完整的座位状态转换接口
3. 座位状态管理
enum SeatStatus {AVAILABLE = 0, // 可用 - 绿色圆圈(O)OCCUPIED = 1, // 已占用 - 红色叉号(X) SELECTED = 2 // 已选择 - 黄色标记(S)
};
可视化座位图示例:
4. 票务管理类 (Ticket
)
设计思路:记录每张票的完整信息,支持退票查询。
class Ticket {
public:int ticketId; // 票号(唯一标识)int showTimeId; // 场次IDint row, col; // 座位位置TicketType type; // 票种(成人/学生/老人)double price; // 实际票价string customerName; // 购票人姓名bool isValid; // 票据有效性
};
四、关键功能实现
1. 智能选座算法
核心挑战:如何处理用户输入的座位格式(如"A5")并转换为数组索引?
// 座位输入解析算法
string seatInput;
cin >> seatInput;char rowChar = seatInput[0]; // 获取行字母
int row = toupper(rowChar) - 'A'; // 转换为数组索引
int col = atoi(seatInput.substr(1).c_str()) - 1; // 列号转索引// 座位可用性检查
if (!cinema.isSeatAvailable(row, col)) {cout << "座位不可用!\n";continue;
}
算法优势:
- 支持大小写字母输入(
toupper
函数处理) - 使用
atoi
和substr
精确解析数字部分 - 自动处理数组索引转换(用户输入从1开始,数组从0开始)
2. 多票种优惠算法
业务逻辑:根据不同票种应用相应折扣策略。
TicketType ticketType;
double discount = 1.0;switch (typeChoice) {case 1: ticketType = ADULT; discount = 1.0; break; // 成人票原价case 2: ticketType = STUDENT; discount = 0.8; break; // 学生票8折case 3: ticketType = SENIOR; discount = 0.6; break; // 老人票6折
}// 总价计算
double totalPrice = selectedSeats.size() * movie.basePrice * discount;
3. 票据生成与管理
设计思路:每张票都有唯一的票号,便于查询和退票操作。
// 批量生成票据
for (size_t i = 0; i < selectedSeats.size(); i++) {Ticket ticket;ticket.ticketId = nextTicketId++; // 自增票号ticket.showTimeId = showChoice;ticket.row = selectedSeats[i].first;ticket.col = selectedSeats[i].second;ticket.type = ticketType;ticket.price = movie.basePrice * discount;ticket.customerName = customerName;ticket.isValid = true;tickets.push_back(ticket);cinema.occupySeat(ticket.row, ticket.col); // 同步更新座位状态
}
4. 退票机制实现
核心功能:通过票号快速定位并处理退票请求。
void refundTicket() {cout << "请输入要退票的票号: ";int ticketId;cin >> ticketId;// 线性查找票据for (size_t i = 0; i < tickets.size(); i++) {if (tickets[i].ticketId == ticketId && tickets[i].isValid) {Ticket& ticket = tickets[i];// 显示票据详情供用户确认displayTicketInfo(ticket);if (confirmRefund()) {ticket.isValid = false; // 标记票据无效cinemas[show.cinemaId].releaseSeat( // 释放座位ticket.row, ticket.col);cout << "退票成功!\n";}return;}}cout << "未找到有效票据!\n";
}
五、完整代码展示
下面是系统的完整源代码,可直接在DevC++ 5.11中编译运行:
#include <iostream>
#include <vector>
#include <string>
#include <map>
#include <iomanip>
#include <algorithm>
#include <cstdlib>using namespace std;// 座位状态枚举
enum SeatStatus {AVAILABLE = 0, // 可用OCCUPIED = 1, // 已占用SELECTED = 2 // 已选择
};// 票类型枚举
enum TicketType {ADULT = 0, // 成人票STUDENT = 1, // 学生票SENIOR = 2 // 老人票
};// 电影信息类
class Movie {
public:string name;string director;int duration; // 分钟string genre;double basePrice;Movie() : name(""), director(""), duration(0), genre(""), basePrice(0.0) {}Movie(const string& n, const string& d, int dur, const string& g, double price): name(n), director(d), duration(dur), genre(g), basePrice(price) {}
};// 场次信息类
class ShowTime {
public:int movieId;int cinemaId;string time;string date;ShowTime() : movieId(0), cinemaId(0), time(""), date("") {}ShowTime(int mid, int cid, const string& t, const string& d): movieId(mid), cinemaId(cid), time(t), date(d) {}
};// 票据类
class Ticket {
public:int ticketId;int showTimeId;int row;int col;TicketType type;double price;string customerName;bool isValid;Ticket() : ticketId(0), showTimeId(0), row(0), col(0), type(ADULT), price(0.0), customerName(""), isValid(true) {}
};// 影厅类
class Cinema {
public:int cinemaId;string name;int rows;int cols;vector<vector<SeatStatus> > seats;Cinema() : cinemaId(0), name(""), rows(0), cols(0) {}Cinema(int id, const string& n, int r, int c) : cinemaId(id), name(n), rows(r), cols(c) {// 初始化座位seats.resize(rows);for (int i = 0; i < rows; i++) {seats[i].resize(cols, AVAILABLE);}}void displaySeats() const {cout << "\n======== " << name << " 座位图 ========\n";cout << " ";for (int j = 0; j < cols; j++) {cout << setw(3) << (j + 1);}cout << "\n";for (int i = 0; i < rows; i++) {cout << setw(2) << (char)('A' + i) << " ";for (int j = 0; j < cols; j++) {char symbol;switch (seats[i][j]) {case AVAILABLE: symbol = 'O'; break;case OCCUPIED: symbol = 'X'; break;case SELECTED: symbol = 'S'; break;default: symbol = '?'; break;}cout << setw(3) << symbol;}cout << "\n";}cout << "\n图例: O-可选 X-已售 S-已选\n";cout << "================================\n";}bool isSeatAvailable(int row, int col) const {if (row < 0 || row >= rows || col < 0 || col >= cols) {return false;}return seats[row][col] == AVAILABLE;}void selectSeat(int row, int col) {if (isSeatAvailable(row, col)) {seats[row][col] = SELECTED;}}void occupySeat(int row, int col) {if (row >= 0 && row < rows && col >= 0 && col < cols) {seats[row][col] = OCCUPIED;}}void releaseSeat(int row, int col) {if (row >= 0 && row < rows && col >= 0 && col < cols) {seats[row][col] = AVAILABLE;}}void clearSelection() {for (int i = 0; i < rows; i++) {for (int j = 0; j < cols; j++) {if (seats[i][j] == SELECTED) {seats[i][j] = AVAILABLE;}}}}
};// 订票系统主类
class TicketSystem {
private:vector<Movie> movies;vector<Cinema> cinemas;vector<ShowTime> showTimes;vector<Ticket> tickets;int nextTicketId;public:TicketSystem() : nextTicketId(1) {initializeData();}void initializeData() {// 初始化电影数据movies.push_back(Movie("阿凡达2", "詹姆斯·卡梅隆", 192, "科幻", 45.0));movies.push_back(Movie("流浪地球3", "郭帆", 173, "科幻", 40.0));movies.push_back(Movie("你好,李焕英", "贾玲", 128, "喜剧", 35.0));movies.push_back(Movie("唐人街探案4", "陈思诚", 136, "悬疑", 38.0));// 初始化影厅数据cinemas.push_back(Cinema(1, "1号厅", 8, 12));cinemas.push_back(Cinema(2, "2号厅", 10, 15));cinemas.push_back(Cinema(3, "3号厅", 6, 10));// 初始化场次数据showTimes.push_back(ShowTime(0, 0, "09:30", "2025-06-08"));showTimes.push_back(ShowTime(0, 1, "14:20", "2025-06-08"));showTimes.push_back(ShowTime(1, 0, "11:45", "2025-06-08"));showTimes.push_back(ShowTime(1, 2, "16:10", "2025-06-08"));showTimes.push_back(ShowTime(2, 1, "19:30", "2025-06-08"));showTimes.push_back(ShowTime(3, 2, "21:15", "2025-06-08"));// 模拟一些已售座位cinemas[0].occupySeat(2, 5);cinemas[0].occupySeat(2, 6);cinemas[0].occupySeat(4, 3);cinemas[1].occupySeat(5, 7);cinemas[1].occupySeat(5, 8);}void displayMovies() const {cout << "\n========== 影片信息 ==========\n";for (size_t i = 0; i < movies.size(); i++) {cout << "影片 " << (i + 1) << ": " << movies[i].name << "\n";cout << " 导演: " << movies[i].director << "\n";cout << " 时长: " << movies[i].duration << "分钟\n";cout << " 类型: " << movies[i].genre << "\n";cout << " 票价: ¥" << fixed << setprecision(1) << movies[i].basePrice << "\n";cout << "------------------------------\n";}}void displayShowTimes() const {cout << "\n========== 排片信息 ==========\n";for (size_t i = 0; i < showTimes.size(); i++) {const ShowTime& show = showTimes[i];const Movie& movie = movies[show.movieId];const Cinema& cinema = cinemas[show.cinemaId];cout << "场次 " << (i + 1) << ": " << movie.name << "\n";cout << " 影厅: " << cinema.name << "\n";cout << " 时间: " << show.date << " " << show.time << "\n";cout << " 票价: ¥" << fixed << setprecision(1) << movie.basePrice << "\n";cout << "------------------------------\n";}}void bookTickets() {displayShowTimes();cout << "\n请选择场次 (1-" << showTimes.size() << "): ";int showChoice;cin >> showChoice;if (showChoice < 1 || showChoice > (int)showTimes.size()) {cout << "无效的场次选择!\n";return;}showChoice--; // 转换为数组索引const ShowTime& selectedShow = showTimes[showChoice];Cinema& cinema = cinemas[selectedShow.cinemaId];const Movie& movie = movies[selectedShow.movieId];cout << "\n您选择的场次:\n";cout << "电影: " << movie.name << "\n";cout << "影厅: " << cinema.name << "\n";cout << "时间: " << selectedShow.date << " " << selectedShow.time << "\n\n";// 显示座位图cinema.displaySeats();vector<pair<int, int> > selectedSeats;cout << "\n请选择座位 (输入行列,如 A5 表示A排5座,输入 0 结束选择):\n";string seatInput;while (true) {cout << "座位: ";cin >> seatInput;if (seatInput == "0") break;if (seatInput.length() < 2) {cout << "座位格式错误!请使用格式如 A5\n";continue;}char rowChar = seatInput[0];int row = toupper(rowChar) - 'A';int col = atoi(seatInput.substr(1).c_str()) - 1;if (!cinema.isSeatAvailable(row, col)) {cout << "座位不可用!\n";continue;}cinema.selectSeat(row, col);selectedSeats.push_back(make_pair(row, col));cinema.displaySeats();}if (selectedSeats.empty()) {cout << "未选择任何座位!\n";cinema.clearSelection();return;}// 选择票种和计算价格cout << "\n请选择票种:\n";cout << "1. 成人票 (原价)\n";cout << "2. 学生票 (8折)\n";cout << "3. 老人票 (6折)\n";cout << "选择 (1-3): ";int typeChoice;cin >> typeChoice;TicketType ticketType;double discount = 1.0;switch (typeChoice) {case 1: ticketType = ADULT; discount = 1.0; break;case 2: ticketType = STUDENT; discount = 0.8; break;case 3: ticketType = SENIOR; discount = 0.6; break;default: cout << "无效选择,默认为成人票\n";ticketType = ADULT; discount = 1.0; break;}// 计算总价double totalPrice = selectedSeats.size() * movie.basePrice * discount;cout << "\n========== 订单确认 ==========\n";cout << "电影: " << movie.name << "\n";cout << "场次: " << selectedShow.date << " " << selectedShow.time << "\n";cout << "影厅: " << cinema.name << "\n";cout << "座位: ";for (size_t i = 0; i < selectedSeats.size(); i++) {cout << (char)('A' + selectedSeats[i].first) << (selectedSeats[i].second + 1);if (i < selectedSeats.size() - 1) cout << ", ";}cout << "\n";string typeStr;switch (ticketType) {case ADULT: typeStr = "成人票"; break;case STUDENT: typeStr = "学生票"; break;case SENIOR: typeStr = "老人票"; break;}cout << "票种: " << typeStr << "\n";cout << "数量: " << selectedSeats.size() << "张\n";cout << "总价: ¥" << fixed << setprecision(2) << totalPrice << "\n";cout << "==============================\n";cout << "确认购买?(y/n): ";char confirm;cin >> confirm;if (confirm == 'y' || confirm == 'Y') {cout << "请输入购票人姓名: ";string customerName;cin >> customerName;// 创建票据for (size_t i = 0; i < selectedSeats.size(); i++) {Ticket ticket;ticket.ticketId = nextTicketId++;ticket.showTimeId = showChoice;ticket.row = selectedSeats[i].first;ticket.col = selectedSeats[i].second;ticket.type = ticketType;ticket.price = movie.basePrice * discount;ticket.customerName = customerName;ticket.isValid = true;tickets.push_back(ticket);cinema.occupySeat(ticket.row, ticket.col);}cout << "\n购票成功!\n";cout << "票号: ";for (size_t i = tickets.size() - selectedSeats.size(); i < tickets.size(); i++) {cout << tickets[i].ticketId;if (i < tickets.size() - 1) cout << ", ";}cout << "\n请妥善保管票号,用于退票!\n";} else {// 取消购买,清除选择cinema.clearSelection();cout << "购买已取消!\n";}}void refundTicket() {if (tickets.empty()) {cout << "暂无票据记录!\n";return;}cout << "请输入要退票的票号: ";int ticketId;cin >> ticketId;bool found = false;for (size_t i = 0; i < tickets.size(); i++) {if (tickets[i].ticketId == ticketId && tickets[i].isValid) {Ticket& ticket = tickets[i];// 显示票据信息const ShowTime& show = showTimes[ticket.showTimeId];const Movie& movie = movies[show.movieId];const Cinema& cinema = cinemas[show.cinemaId];cout << "\n========== 票据信息 ==========\n";cout << "票号: " << ticket.ticketId << "\n";cout << "购票人: " << ticket.customerName << "\n";cout << "电影: " << movie.name << "\n";cout << "场次: " << show.date << " " << show.time << "\n";cout << "影厅: " << cinema.name << "\n";cout << "座位: " << (char)('A' + ticket.row) << (ticket.col + 1) << "\n";cout << "票价: ¥" << fixed << setprecision(2) << ticket.price << "\n";cout << "==============================\n";cout << "确认退票?(y/n): ";char confirm;cin >> confirm;if (confirm == 'y' || confirm == 'Y') {ticket.isValid = false;cinemas[show.cinemaId].releaseSeat(ticket.row, ticket.col);cout << "退票成功!退款金额: ¥" << fixed << setprecision(2) << ticket.price << "\n";} else {cout << "退票已取消!\n";}found = true;break;}}if (!found) {cout << "未找到有效票据!\n";}}void displayMyTickets() const {cout << "请输入购票人姓名: ";string name;cin >> name;cout << "\n========== " << name << " 的票据 ==========\n";bool hasTickets = false;for (size_t i = 0; i < tickets.size(); i++) {const Ticket& ticket = tickets[i];if (ticket.customerName == name && ticket.isValid) {const ShowTime& show = showTimes[ticket.showTimeId];const Movie& movie = movies[show.movieId];const Cinema& cinema = cinemas[show.cinemaId];cout << "票号: " << ticket.ticketId << "\n";cout << "电影: " << movie.name << "\n";cout << "场次: " << show.date << " " << show.time << "\n";cout << "影厅: " << cinema.name << "\n";cout << "座位: " << (char)('A' + ticket.row) << (ticket.col + 1) << "\n";cout << "票价: ¥" << fixed << setprecision(2) << ticket.price << "\n";cout << "------------------------------\n";hasTickets = true;}}if (!hasTickets) {cout << "未找到有效票据!\n";}}void displayMenu() const {cout << "\n========== 影院订票系统 ==========\n";cout << "1. 查看影片信息\n";cout << "2. 查看排片信息\n";cout << "3. 购票\n";cout << "4. 退票\n";cout << "5. 查看我的票据\n";cout << "0. 退出系统\n";cout << "=================================\n";cout << "请选择功能 (0-5): ";}void run() {cout << "欢迎使用影院订票系统!\n";int choice;do {displayMenu();cin >> choice;switch (choice) {case 1:displayMovies();break;case 2:displayShowTimes();break;case 3:bookTickets();break;case 4:refundTicket();break;case 5:displayMyTickets();break;case 0:cout << "感谢使用影院订票系统!再见!\n";break;default:cout << "无效选择,请重新输入!\n";break;}if (choice != 0) {cout << "\n按回车键继续...";cin.ignore();cin.get();}} while (choice != 0);}
};int main() {TicketSystem system;system.run();return 0;
}
影片信息:
排片信息:
购票:
六、总结与扩展建议
1. 项目成果总结
通过本项目的开发,我们成功实现了一个功能完整的影院订票系统:
✅ 实现的核心功能:
- 电影信息展示与排片管理
- 可视化座位图与智能选座
- 多票种优惠策略与票价计算
- 完整的购票流程与电子票据生成
- 退票功能与座位状态同步更新
- 多影厅支持与票据查询管理
2. 性能优化建议
🚀 算法优化:
// 当前:线性查找票据 O(n)
// 优化:使用map<int, Ticket>按票号索引 O(log n)
map<int, Ticket> ticketMap;// 当前:座位状态实时计算
// 优化:缓存可用座位数量,减少重复计算
int availableSeatsCount;
💾 数据持久化:
// 扩展:文件存储支持
void saveToFile(const string& filename);
void loadFromFile(const string& filename);// 扩展:JSON格式数据交换
void exportToJSON(const string& filename);
void importFromJSON(const string& filename);
3. 功能扩展方向
🎬 业务功能扩展:
-
高级排片管理
- 支持多日期排片
- 自动计算场次间隔
- 热门电影智能推荐位置
-
用户体验优化
- 座位推荐算法(最佳观影位置)
- 批量选座功能
- 座位锁定机制(防止冲突)
-
商业功能完善
- 会员积分系统
- 套餐优惠(爆米花+饮料)
- 团体票优惠策略
🔧 技术架构升级:
// 设计模式应用
class TicketPriceStrategy { // 策略模式
public:virtual double calculatePrice(double basePrice) = 0;
};class Observer { // 观察者模式
public:virtual void update(const string& event) = 0;
};class TicketFactory { // 工厂模式
public:static Ticket* createTicket(TicketType type);
};
4. 学习价值与应用场景
📚 教育价值:
- C++基础语法:类、继承、多态、STL容器
- 软件工程实践:需求分析、架构设计、模块化开发
- 算法与数据结构:二维数组、枚举、查找算法
- 项目管理经验:版本控制、代码规范、文档编写
💡 小贴士:想要快速运行本项目?只需要:
- 打开DevC++ 5.11
- 新建项目,复制完整代码
- 按
F11
编译运行 - 开始体验完整的订票流程!
🔗 项目源码:完整代码已在文中提供,支持直接复制使用。如有问题,欢迎在评论区交流讨论!
创作者:Code_流苏(CSDN)(一个喜欢古诗词和编程的Coder😊)