第11章 综合项目——酒店管理系统

学习目标

★ 了解项目的需求分析

★ 掌握C++语言模块化设计开发

★ 掌握项目的调试

★ 总结项目心得

本书的第1~10章已对C++语言基本知识进行了详细讲解。学习编程语言的目的是将其应用到项目开发中解决实际问题,在不断的应用中增强开发技能,锻炼编程思维,加深对程序设计语言的认识和理解。本章将带领大家使用C++语言实现一个简单的酒店管理系统项目开发。

11.1 项目分析

开发项目前要进行充分的产品调研,了解项目应用领域的业务流程、操作规范,在明确项目需求的基础上进行详细设计,确定项目功能,才能开发出满足实际需求的软件产品。本节将针对酒店管理系统功能描述和项目设计进行介绍。

11.1.1 功能描述

本章将实现一个简单的酒店管理系统,用以实现酒店客房的显示、添加、删除、状态设置等操作。系统分为3个功能模块,具体如图11-1所示。

下面结合图11-1介绍酒店管理系统各个模块的主要功能。

(1)客房模块:主要包括3个功能,分别是获取客房数据,如客房编号、名称、价格、面积、床位数量、状态(空闲或入住)等,将客房数据保存到外部文件,以及在显示客房信息时从文件中读取客房数据。

(2)客房管理模块:主要用于管理客房。例如,添加客房、删除客房、查找客房、设置客房状态等。

(3)界面模块:主要用于显示酒店管理系统的操作界面。例如,启动系统、显示菜单、添加客房、删除客房、设置客房状态及显示所有客房信息等操作界面。

图11-1 酒店管理系统模块划分

11.1.2 项目设计

确定系统功能并划分功能模块之后,需要根据功能模块进行类的设计。下面分模块讲解类的设计。

1. 客房模块

客房管理模块主要负责保存、读取客房数据,如客房编号、名称、价格等,该模块需要设计一个客房类GuestRoom,在该类中定义客房的数据信息作为成员变量,并提供访问这些客房数据的成员函数。此外,GuestRoom类负责将客房数据保存到本地文件中,并在显示客房信息时负责从文件读取客房信息,因此还需要定义文件读写函数。

GuestRoom类的详细设计如图11-2所示。

2. 客房管理模块

客房管理模块主要负责管理客房,如添加客房、删除客房、查找客房,以及设置客房状态等,该模块需要设计一个客房管理类GuestR oomM anager,在该类中定义各种功能函数。由于管理客房时每次需要从文件中读取客房数据,在设计时可以将读取的客房数据保存到一个m ap容器中,以客房编号为K ey键,以GuestRoom类对象作为Value值。因此,GuestRoomM anager类中还需要定义一个m ap容器作为成员变量存储客房信息。

图11-2 GuestRoom类的详细设计

GuestRoomM anager类的详细设计如图11-3所示。

3. 界面模块

界面模块主要负责显示酒店管理系统的操作界面,包括显示菜单、添加客房、删除客房、设置客房状态、显示所有客房信息等操作界面,该模块需要设计一个界面类RoomView,定义功能函数为各种操作提供界面显示。

RoomV iew类的详细设计如图11-4所示。

图11-3 G uestRoom M anager类的详细设计

图11-4 Room View类的详细设计

上述3个类就是酒店管理系统需要的所有类,这3个类之间的关系如图11-5所示。

图11-5 酒店管理系统类之间的关系

11.2 项目实现

11.1节对酒店管理系统的功能进行了描述与模块划分,并且针对每个模块都进行了类的设计。本节就带领读者实现各个模块。

11.2.1 客房模块的实现

在实际开发中,一个模块通常分两个文件实现,类的定义与数据的设计在.h文件中,而类的实现在对应的.cpp文件中。下面分两个文件实现客房模块:guest_room.h和guest_room.cpp,在guest_room.h文件中定义GuestRoom类,在guest_room.cpp文件中实现GuestRoom类的设计,具体如例11-1和例11-2所示。

例11-1 guest_room.h

#pragma once 
#include <string> 
#include<map> 
using namespace std; 
//定义enum ROOM_STATE枚举类型,表示客房状态:空闲、入住 
enum ROOM_STATE { FREE = 0, CHECK_IN }; 
//定义客房类 
class GuestRoom 
{ 
public: 
 GuestRoom()=default;       //提供默认构造函数 
 GuestRoom(string, int, int, int,ROOM_STATE);  //有参构造函数 
 string generate_number();      //生成客房编号 
 string show_state();       //显示客房状态 
   bool save_data(map<string, GuestRoom>&);   //保存数据 
  map<string, GuestRoom> read_data();    //读取数据 
public: 
 string get_num();        //获取客房编号 
 string get_name();        //获取客房名称 
 int get_price();        //获取客房价格 
 int get_area();        //获取客房面积 
 int get_bed_num();        //获取客房床位数量 
 void set_state();        //设置客房状态 
private: 
 string m_number;        //客房编号 
 int m_price;         //客房价格 
 int m_area;         //客房面积 
 int m_bed_number;        //客房床位数量 
 string m_name;         //客房名称 
 enum  ROOM_STATE m_state;      //客房状态 
}; 

例11-2 guest_room.cpp

 1  #define _CRT_SECURE_NO_WARNINGS 
 2  #include"guest_room.h" 
 3  #include<time.h> 
 4  #include<iostream> 
 5  #include<fstream> 
 6  using namespace std; 
 7  //定义字符指针,指向一个文件 
 8  const char* const room_data_file = "room.dat"; 
 9  //GuestRoom构造函数实现 
 10 GuestRoom::GuestRoom(string name, int price , int bed_num, 
 11      int area,ROOM_STATE state=FREE) 
 12 { 
 13      this->m_name = name; 
 14      this->m_price = price; 
 15      this->m_bed_number = bed_num; 
 16      this->m_area = area; 
 17      this->m_number = generate_number(); 
 18      this->m_state = state; 
 19 } 
 20 //生成客房编号 
 21 string GuestRoom::generate_number() 
 22 { 
 23      //本地时间转字符串 
 24      time_t my_time = time(NULL); 
 25      struct tm* my_tm = localtime(&my_time); 
 26      char tim_buff[128] = { 0 }; 
 27      sprintf(tim_buff, "%d%d", my_tm->tm_yday, my_tm->tm_sec); 
 28      //生成随机数 
 29      int rand_num = rand() % 50; 
 30      char buf[128] = { 0 }; 
 31      sprintf(buf, "%d", rand_num); 
 32      //拼接字符串作为房间编号 
 33      return string(tim_buff) + string(buf); 
 34 } 
 35 //显示客房状态 
 36 string GuestRoom::show_state() 
 37 { 
 38      if (m_state == FREE) 
 39           return "空闲"; 
 40      if (m_state == CHECK_IN) 
 41           return "入住"; 
 42 } 
 43 //保存数据(map容器,key:客房编号,value:客房对象) 
 44 bool GuestRoom::save_data(map<string, GuestRoom>& room_list) 
 45 { 
 46      //1. 打开文件 
 47      ofstream ofs(room_data_file,ios::out); 
 48      if(!ofs) 
 49      { 
 50           return false; 
 51      } 
 52      //2. 写入对象数据 
 53      for(auto& room : room_list) 
 54      { 
 55           //写入m_name成员变量所占内存大小及其值 
 56           size_t name_len = room.second.m_name.size(); 
 57           ofs.write(reinterpret_cast<const char*>(&name_
 58                sizeof(size_t)); 
 59           ofs.write(room.second.m_name.c_str(), name_len); 
 60           //写入m_price 
 61           ofs.write(reinterpret_cast<const char*>(&room.second.m_price),  
 62                sizeof(int)); 
 63           //写入m_area 
 64           ofs.write(reinterpret_cast<const char*>(&room.second.m_area), 
 65                sizeof(int)); 
 66           //写入m_bed_number 
 67           ofs.write(reinterpret_cast<const  char*> 
 68                 (&room.second.m_bed_number),sizeof(int)); 
 69           //写入m_state 
 70           ofs.write(reinterpret_cast<const char*>(&room.second.m_state),  
 71                sizeof(enum ROOM_STATE)); 
 72           //写入m_number成员变量所占内存大小及其值 
 73           size_t number_len = room.second.m_number.size(); 
 74           ofs.write(reinterpret_cast<const  
 75                     char*>(&number_len), sizeof(size_t)); 
 76           ofs.write(room.second.m_number.c_str(), number_len); 
 77      } 
 78      //3. 关闭文件  
 79      ofs.close(); 
 80      return true; 
 81 } 
 82 //读取数据 
 83 map<string, GuestRoom>  GuestRoom::read_data() 
 84 { 
 85      //1. 打开文件 
 86      map<string, GuestRoom> room_list; 
 87      ifstream ifs(room_data_file, ios::in); 
 88      if(!ifs) 
 89      { 
 90           return room_list; 
 91      } 
 92      //2. 写入对象数据 
 93      while (ifs.peek() != EOF) 
 94      { 
 95           GuestRoom room; 
 96           //读入m_name成员变量所占内存大小及其值 
 97           size_t name_len = 0; 
 98           ifs.read(reinterpret_cast<char*>(&name_len), sizeof(size_t)); 
 99           char name_buffer[128] = { 0 }; 
 100       ifs.read(name_buffer, name_len); 
 101       room.m_name = name_buffer; 
 102       //写入m_price 
 103       ifs.read(reinterpret_cast<char*>(&room.m_price),sizeof(int)); 
 104       //写入m_area 
 105       ifs.read(reinterpret_cast<char*>(&room.m_area),sizeof(int)); 
 106       //写入 bed_number 
 107       ifs.read(reinterpret_cast<char*>(&room.m_bed_number), 
 108        sizeof(int)); 
 109       //写入 state 
 110       ifs.read(reinterpret_cast<char*>(&room.m_state),  
 111       sizeof(enum ROOM_STATE)); 
 112       //写入m_number成员变量所占内存大小及其值 
 113       size_t number_len = 0; 
 114       ifs.read(reinterpret_cast<char*>(&number_len),sizeof(size_t)); 
 115       char number_buffer[128] = { 0 }; 
 116       ifs.read(number_buffer, number_len); 
 117       room.m_number = number_buffer; 
 118       //将数据存储到map容器中 
 119       room_list.insert(make_pair(room.m_number, room)); 
 120        } 
 121   //3. 关闭文件  
 122   ifs.close();
 123        return room_list; 
 124  } 
 125  //获取客房编号 
 126  string GuestRoom::get_num() 
 127  { 
 128       return m_number; 
 129  } 
 130  //设置客房状态 
 131  void GuestRoom::set_state() 
 132  { 
 133       m_state = CHECK_IN; 
 134  } 
 135  //获取客房名称 
 136  string GuestRoom::get_name() 
 137  { 
 138       return m_name; 
 139  } 
 140  //获取客房价格 
 141  int GuestRoom::get_price() 
 142  { 
 143       return m_price; 
 144  } 
 145  //获取客房面积 
 146  int GuestRoom::get_area() 
 147  { 
 148  return m_area; 
 149  } 
 150  //获取客房床位数量 
 151  int GuestRoom::get_bed_num() 
 152  { 
 153       return m_bed_number; 
 154  } 

guest_room.cpp文件一共实现了11个函数,下面分别对这些函数进行介绍。

(1)GuestRoom()函数

第10~19行代码实现了GuestRoom()函数,GuestRoom()函数是类的构造函数,用于对类的成员变量进行初始化,实现比较简单。

(2)generate_number()函数

第21~34行代码实现了generate_number()函数,generate_number()函数用于生成客房编号。第24~27行代码调用localtim e()函数获取本地时间,并通过sprintf()函数将获取的本地时间转换成字符串存储到tim_buff字符数组中。第29~31行代码调用random()函数生成一个小于50的随机数,并通过sprintf()函数将随机数转换成字符串存储到buf字符数组中。第33行代码将字符数组tim_buff与字符数组buf中的数据拼接后作为房间编号并返回。

(3)show_state()函数

第36~42行代码实现了show_state()函数,show_state()函数用于显示客房状态。在函数内部,通过if语句判断成员变量m_state的值返回客房状态。客房状态一共有两种:空闲和入住。

(4)save_data()函数

第44~81行代码实现了save_data()函数,save_data()函数用于将客房数据保存到外部文件中,它有一个m ap<string,GuestRoom>类型的容器作为参数。在函数内部,第47~51行代码以追加方式打开文件,并判断文件打开是否成功。第53~77行代码通过for循环依次将客房的名称、价格、面积、床位数量、状态和编号写入文件。第79行代码关闭文件。

(5)read_data()函数

第83~124行代码实现read_data()函数,read_data()函数从文件中读取客房数据,并存储到m ap容器中,将m ap容器返回。第86行代码定义m ap容器room_list。第87~91行代码打开文件,并判断文件是否打开成功。第93~120行代码通过while循环从文件中依次读取客房的编号、状态、名称、价格、面积及床位数量,并存储到容器room_list中。第122行代码关闭文件。

(6)get_num()函数

第126~129行代码实现get_num()函数,用于获取客房编号。

(7)set_state()函数

第131~134行代码实现set_state()函数,用于设置客房状态。

(8)get_nam e()函数

第136~139行代码实现get_nam e()函数,用于获取客房名称。

(9)get_price()函数

第141~144行代码实现get_price()函数,用于获取客房价格。

(10)get_area()函数

第146~149行代码实现get_area()函数,用于获取客房面积。

(11)get_bed_num()函数

第151~154行代码实现get_bed_num()函数,用于获取客房床位数量。

11.2.2 客房管理模块的实现

客房管理模块分两个文件实现:room_m anager.h和room_m anager.cpp,room_m anager.h文件用于定义GuestRoomM anager类,room_m anager.cpp文件用于实现GuestRoomM anager类的成员函数,具体如例11-3和例11-4所示。

例11-3 room_manager.h

#pragma once 
#include "guest_room.h" 
#include "room_manager.h" 
#include<map> 
#include<string> 
class GuestRoomManager      //定义客房管理类 
{ 
public: 
 GuestRoomManager();      //构造函数 
 bool check_in(const string);    //查找客房 
 void add_room(const GuestRoom room);   //添加客房 
 bool remove_data(const string);    //删除客房数据 
 void set_room_state(string number);  //设置客房状态 
private: 
 map<string, GuestRoom> m_room_list;  //map容器 
}; 

例11-4 room_manager.cpp

 1  #include "room_manager.h" 
 2  #include "guest_room.h" 
 3  #include<iostream> 
 4  using namespace std; 
 5  //构造函数实现 
 6  GuestRoomManager::GuestRoomManager() 
 7  { 
 8       GuestRoom().read_data();//读取客房数据  
 9  } 
 10 //查找客房 
 11 bool GuestRoomManager::check_in(const string number) 
 12 { 
 13      //读取文件中的数据到容器中 
 14      GuestRoom grm; 
 15      m_room_list = grm.read_data(); 
 16      //在容器中查找客房 
 17      if(m_room_list.find(number) == m_room_list.end()) 
 18      { 
 19           return false; 
 20      } 
 21      return true; 
 22 } 
 23 //添加客房 
 24 void GuestRoomManager::add_room( GuestRoom room) 
 25 { 
 26      //判断客房编号是否存在 
 27      if (check_in(room.get_num())) 
 28      { 
 29           cout << "房间编号已存在" << endl; 
 30           return; 
 31      } 
 32      //将客房添加到容器中 
 33      m_room_list.insert(make_pair(room.get_num(), room)); 
 34      //将容器中的数据存放到文件中 
 35      GuestRoom().save_data(m_room_list); 
 36 } 
 37 //删除客房数据 
 38 bool GuestRoomManager::remove_data(const string number) 
 39 { 
 40      //判断客房编号是否存在 
 41      if (!check_in(number)) 
 42      { 
 43           cout << "房间编号不存在" << endl; 
 44           return false; 
 45      } 
 46      //删除 
 47      m_room_list = GuestRoom().read_data(); 
 48      m_room_list.erase(number); 
 49      //更新文件 
 50      GuestRoom().save_data(m_room_list); 
 51      return true; 
 52 } 
 53 //设置客房状态 
 54 void GuestRoomManager::set_room_state(string number) 
 55 { 
 56      if (!check_in(number)) 
 57      { 
 58           cout << "房间编号不存在" << endl; 
 59           return; 
 60      } 
 61      else 
 62      { 
 63           //将文件中的客房数据读取到文件中 
 64           m_room_list = GuestRoom().read_data(); 
 65           //设置为入住状态 
 66           m_room_list[number].set_state(); 
 67           //将数据保存到文件中 
 68           GuestRoom().save_data(m_room_list); 
 69      } 
 70 }

room_m anager.cpp文件一共实现了5个函数,下面分别进行介绍。

(1)GuestRoomM anager()函数

第6~9行代码实现GuestRoomM anager()函数,GuestRoomM anager()函数是类的构造函数。在构造函数内部,通过GuestRoom类的匿名对象调用read_data()函数读取文件,即每次通过GuestRoomM anager类对象管理客房时,都会先读取文件获取客房数据。

(2)check_in()函数

第11~22行代码实现check_in()函数,check_in()函数用于查找客房,参数为客房编号。第14~15行代码创建GuestRoom类对象grm,并通过对象grm调用read_data()函数,将客房数据读取到容器m_room_list中。第17~20行代码,通过容器m_room_list调用find()函数查找客房编号是否存在,如果不存在,则返回false,否则返回true。

(3)add_room()函数

第24~36行代码实现add_room()函数。add_room()函数用于添加客房,参数为GuestRoom类对象,即要添加的客房。第27~31行代码判断客房编号是否已经存在,如果客房编号已经存在,则退出函数调用。第33行代码通过容器m_room_list调用insert()函数将客房添加到容器中。第35行代码通过GuestRoom类匿名对象调用save_data()函数将容器m_room_list的数据保存到文件中。

(4)rem ove_data()函数

第38~52行代码实现rem ove_data()函数,rem ove_data()函数用于删除客房数据,参数为要删除客房的编号。第41~45行代码通过check_in()函数判断客房编号是否存在,如果不存在,就返回false。第47~48行代码通过GuestRoom类匿名对象调用read_data()函数,将文件中的数据读取到容器m_room_list当中,并调用容器m_room_list的成员函数erase()函数删除编号为number的客房。第50行代码通过GuestRoom类匿名对象调用save_data()函数将容器m_room_list中更改后的客房数据保存到文件中。

(5)set_room_state()函数

第54~70行代码实现set_room_state()函数,set_room_state()函数用于设置客房状态,参数为客房编号。第56~69行代码判断客房编号是否存在,如果不存在,则显示提示信息后返回;如果存在,则通过GuestRoom类匿名对象调用read_data()函数,将文件中的客房数据读取到容器m_room_list中。第66行代码在容器m_room_list中设置客房状态,第68行代码通过GuestRoom类匿名对象调用save_data()函数,将更改后的客房数据重新保存到文件中。

11.2.3 界面模块的实现

界面模块分两个文件实现:room_view.h和room_view.cpp。room_view.h文件用于定义GuestView类,room_view.cpp文件用于实现GuestV iew类的成员函数,具体如例11-5和例11-6所示。

例11-5 room_view.h

#pragma once 
#include "room_view.h" 
#include "room_manager.h" 
class RoomView    //定义界面类 
{ 
public: 
 void show_menu();   //显示菜单 
 void add_room();   //添加客房 
 void remove_data();   //删除客房 
 void set_room();   //设置客房状态 
 void show_all();   //显示所有客房信息 
 void run();    //启动酒店管理系统 
private: 
 GuestRoomManager m_grm;   //客房管理对象 
};

例11-6 room_view.cpp

 1  #include<iostream> 
 2  #include "room_view.h" 
 3  #include<iomanip> 
 4  using namespace std; 
 5  //显示菜单 
 6  void RoomView::show_menu() 
 7  { 
 8       cout << "1. 显示客房信息" << endl; 
 9       cout << "2. 设置客房状态" << endl; 
 10      cout << "3. 删除客房信息" << endl; 
 11      cout << "4. 添加客房信息" << endl; 
 12      cout << "5. 退出管理系统" << endl; 
 13 } 
 14 // 添加客房 
 15 void RoomView::add_room() 
 16 { 
 17      string name; 
 18      int price; 
 19      int bed_num; 
 20      int area; 
 21      cout << "请输入客房名称:"; cin >> name; 
 22      cout << "请输入客房价格:"; cin >> price; 
 23      cout << "请输入客房床数量:"; cin >> bed_num; 
 24      cout << "请输入客房面积:"; cin >> area; 
 25      //创建客房对象 
 26      GuestRoom room(name, price, bed_num, area,FREE); 
 27      //调用客房管理模块的add_room()函数添加客房 
 28      m_grm.add_room(room); 
 29 } 
 30 //删除客房 
 31 void RoomView::remove_data() 
 32 { 
 33      cout << "请输入您要删除的客房编号:"; 
 34      string room_number; 
 35      cin >> room_number; 
 36      // 调用客房管理模块的remove_data()函数 
 37      m_grm.remove_data(room_number); 
 38 } 
 39 //设置客房状态 
 40 void RoomView::set_room() 
 41 { 
 42      string number; 
 43      cout << "请输入要设置的客房编号:"; cin >> number; 
 44      m_grm.set_room_state(number); 
 45 } 
 46 //显示所有客房信息 
 47 void RoomView::show_all() 
 48 {  
 49  
 50      cout << "------------------------------------------------" << endl; 
 51      cout << "编号" << "\t   " << "名称" << "\t\t" << "面积" << "\t" 
 52           << "价格" << "\t" << "床位数量" << "\t" << "状态" << endl; 
 53      cout << "-------------------------------------------------" << endl; 
 54      GuestRoom grm; 
 55      map<string, GuestRoom> rooms; 
 56      rooms=grm.read_data(); 
 57      if(rooms.empty()) 
 58      { 
 59           cout<<"请添加客房信息后再进行操作!"<<endl; 
 60      } 
 61      for (auto& room : rooms) 
 62      { 
 63           cout << room.second.get_num() << "\t" << setw(10) 
 64                << room.second.get_name() << "\t"<< room.second.get_area()  
 65                << "平方\t" << room.second.get_price() << "元\t  " 
 66                << room.second.get_bed_num() << "个\t\t"  
 67                << room.second.show_state() << endl; 
 68      } 
 69      cout << "-------------------------------------------------" << endl; 
 70 } 
 71 //启动酒店管理系统 
 72 void RoomView::run() 
 73 { 
 74      //定义state变量标识系统是否启动 
 75      bool state = true; 
 76      while (state) 
 77      { 
 78           //显示菜单
 79           show_menu(); 
 80           //获得输入的命令 
 81           int flag = -1; 
 82           cout << "请输入您的操作:"; 
 83           cin >> flag; 
 84           system("cls"); 
 85           //根据输入执行相应的操作 
 86           switch (flag) 
 87           { 
 88           case 1: 
 89                show_all(); 
 90                break; 
 91           case 2: 
 92                set_room(); 
 93                break; 
 94           case 3: 
 95                remove_data(); 
 96                break; 
 97           case 4: 
 98                add_room(); 
 99                break; 
 100                case 5: 
 101                     exit(0); 
 102                default: 
 103                     break; 
 104                } 
 105           } 
 106      } 

room_view.cpp文件一共实现了6个函数,下面分别进行介绍。

(1)show_m enu()函数

第6~13行代码实现show_m enu()函数,show_m enu()函数用于显示酒店管理系统菜单,实现比较简单,没有业务逻辑,这里不再赘述。

(2)add_room()函数

第15~29行代码实现add_room()函数,add_room()函数用于添加客房。第17~24行代码定义变量并输出添加客房数据的提示。第26行代码创建GuestRoom类对象room。第28行代码通过对象m_grm调用函数add_room()添加客房对象room。

(3)rem ove_data()函数

第31~38行代码实现rem ove_data()函数,rem ove_data()函数用于删除客房。在函数内部,首先输入要删除的客房编号,然后通过对象m_grm调用成员函数rem ove_data()删除客房数据。

(4)set_room()函数

第40~45行代码实现set_room()函数,set_room()函数用于设置客房状态。在函数内部,首先输入客房编号,然后通过对象m_grm调用成员函数set_room_state()设置客房状态。

(5)show_all()函数

第47~70行代码实现show_all()函数,show_all()函数用于显示所有客房信息。第50~53行代码显示客房信息的输出格式。第54~56行代码创建GuestRoom类对象grm,m ap<string,Guest>对象room s,并通过对象grm调用read_data()函数,将文件中的客房数据读取到容器room s中。第57~60行代码判断room s是否为空,如果为空,则提示先添加客房再进行其他操作。第61~68行代码通过for循环遍历容器room s,依次输出所有客房信息。

(6)run()函数

第72~106行代码实现run()函数,run()函数用于启动酒店管理系统。在函数内部,通过while循环不断执行操作。第79行代码调用show_m enu()函数显示菜单。第81~83行代码定义int类型变量flag,并从键盘输入flag的值。第86~104行代码通过输入判断要执行的操作,如果输入1,就调用show_all()函数显示所有客房信息;如果输入2,就调用set_room()函数设置客房状态;如果输入3,就调用rem ove_data()函数删除客房;如果输入4,就调用add_room()函数添加客房;如果输入5,就调用exit()函数退出系统。

11.2.4 main()函数实现

前面已经完成了酒店管理系统中所有功能模块的编写,但是功能模块是无法独立运行的,需要一个程序将这些功能模块按照项目的逻辑思路整合起来,这样才能完成一个完整的项目。此时就需要创建一个m ain.cpp文件来整合这些功能模块,m ain.cpp文件中包含m ain()函数,是程序的入口。m ain.cpp文件的实现如例11-7所示。

例11-7 main.cpp

 1  #include "guest_room.h" 
 2  #include<iostream> 
 3  #include<time.h> 
 4  #include<string> 
 5  #include "room_manager.h" 
 6  #include "room_view.h" 
 7  using namespace std; 
 8  int main() 
 9  { 
 10      RoomView grv;   //创建RoomView类对象 
 11      grv.run();   //启动酒店管理系统 
 12      return 0; 
 13 } 

m ain.cpp文件实现比较简单,在m ain()函数中,首先创建了RoomView类对象grv,然后通过对象grv调用run()函数启动酒店管理系统。

至此,酒店管理系统代码已经全部完成。

11.3 效果显示

11.2节实现了整个项目,为了让读者更加直观地看到酒店管理系统最终的效果,并对程序的执行过程有个整体的认识,下面分别展示酒店管理系统的界面效果。

1. 显示系统主菜单

系统运行后,显示系统主菜单,如图11-6所示。

图11-6 系统主菜单

2. 显示客房信息

输入数字1,显示客房信息,第一次运行程序时,由于还没有客房,因此显示客房信息时,没有任何信息,系统会提示先添加客房,如图11-7所示。

3. 添加客房信息

输入数字4,添加客房信息,如图11-8所示。

4. 显示客房信息

添加客房之后,再次输入数字1,显示客房信息,如图11-9所示。

图11-7 第一次运行程序显示客房信息

图11-8 添加客房信息

图11-9 显示客房信息

5. 设置客房状态

输入数字2,可以设置客房状态。在设置客房状态时,需要输入客房编号,如图11-10所示。

图11-10 设置客房状态

输入客房编号之后,系统会更改该编号的客房状态。回到主菜单再次输入数字1,显示客房信息,可以看到92317客房状态改变,如图11-11所示。

6. 删除客房信息

输入数字3,可以删除客房信息。在删除客房信息时,输入客房编号,如图11-12所示。

输入客房编号后,按Enter键回到主菜单,系统会删除对应编号的客房信息。此时,再次输入数字1查看客房信息,编号为922134的客房不存在,如图11-13所示。

图11-11 查看客房状态

图11-12 删除客房信息

图11-13 查看客房信息

7. 退出管理系统

输入数字5,可以退出酒店管理系统,如图11-14所示。

图11-14 退出酒店管理系统

11.4 程序调试

在程序开发过程中难免会出现各种各样的错误。为了快速发现和解决程序中的错误,可以使用Visual Studio 2019自带的调试功能,通过程序调试快速定位错误。本节就以酒店管理系统为例对调试功能进行详细的讲解。

11.4.1 设置断点

在程序的调试过程中,为了分析出程序出错的原因,往往需要观察程序中某些数据的变化情况,这时就需要为程序设置断点,从而让正在运行的程序在断点处暂停,方便观察程序中的数据。

在V isual Studio 2019中,为程序设置断点的方式有两种,下面分别介绍。

1. 单击鼠标右键

在程序中,将鼠标放置在要插入断点的代码行,单击鼠标右键,依次选择【断点】→【插入断点】,如图11-15所示。

在图11-15中单击【插入断点】选项后,选中的代码行左边会有一个红色的圆点,如图11-16所示。

图11-15 单击鼠标右键插入断点

图11-16 插入断点

从图11-16得知,第11行代码处添加了一个断点。为程序设置断点以后,就可以对程序进行调试了。调试完毕要删除断点。删除断点的操作也非常简单,将鼠标放置在断点代码行,单击鼠标右键,选择【删除断点】,如图11-17所示。

图11-17 删除断点

2. 单击鼠标左键

除了上述方式,读者还可以在代码左边的灰色区域通过单击鼠标左键插入断点,断点插入成功后,代码行左侧也会有红色圆点出现。同样,删除断点时,只需再次在代码行左侧已插入的红色圆点处单击鼠标左键,即可删除断点。相比于上一种断点插入方式,这种方式更简单便捷。

11.4.2 单步调试

当程序出现Bug时,为了找出错误原因,通常会一步一步跟踪程序的执行流程,这种调试方式称为单步调试。单步调试分为逐语句调试和逐过程调试。逐语句调试会进入函数内部调试,单步执行函数体的每条语句;逐过程调试不会进入函数体内部,而是把函数当作一步来执行。下面分别对这两种调试方法进行介绍。

1. 逐语句调试

以图11-16中的断点为例对项目进行逐语句调试,设置断点之后,单击工具栏中的运行按钮,程序运行之后,遇到断点就会停止执行,如图11-18所示。

图11-18 程序遇到断点停止执行

在图11-18中,程序遇到断点会暂停运行,等待用户进行操作。程序暂停运行之后,Visual Studio 2019工具栏按钮会发生变化,如图11-19所示。

图11-19 Visual Studio 2019为调试按钮

图11-19中的调试按钮相关介绍具体如下。

如果在调试时想逐语句调试,则使用快捷键【F11】或工具栏中的逐语句调试按钮,程序会进入run()函数内部一条一条地执行语句。逐语句调试过程如图11-20所示。

图11-20 逐语句调试

在图11-20中,使用快捷键【F11】或工具栏中的逐语句调试按钮,程序就会逐条语句往下执行,当执行第78行代码,就会接着进入show_m enu()函数执行。

2. 逐过程调试

逐过程调试在每次调试时执行一个函数,当调试开始时,使用快捷键【F10】或单击工具栏中的逐过程调试按钮,可以一次执行一个函数,程序会逐个函数地往下执行,直到程序执行完毕。

调试程序一般是为了查找错误,当查找到错误之后就会结束调试,并不会全程调试。结束调试后,可单击工具栏中的运行按钮继续往下执行程序,也可以单击工具栏中的停止调试按钮结束程序调试。

11.4.3 观察变量

在程序调试过程中,主要的就是观察当前变量的值以尽快找到程序出错的原因,Visual Studio 2019工具支持多种方式查看变量,下面介绍几个常用的查看变量的方法。

1. 使用鼠标悬停法查看变量的值

Visual Studio 2019可以通过鼠标悬停的方式查看变量的值,即鼠标指向变量,变量就会显示出其值。例如,run()函数中定义了变量flag,下面以查看变量flag的值为例演示Visual Studio 2019查看变量的方法。在图11-20中,程序还未执行第82行代码,此时变量flag的值为-1,将鼠标悬停在变量flag上面,Visual Studio 2019会显示出flag的值,如图11-21所示。

在图11-21中,通过鼠标悬停方式查看到变量flag的值为-1。继续逐语句往下执行,当执行完第82行代码时,通过键盘输入flag的值,此时再查看flag的值,flag就变成了输入的数值,如图11-22所示。

在图11-22中,程序执行到了第83行,cin>>flag语句已经执行完毕,此时将鼠标悬停在变量flag上,V isual Studio 2019会显示flag的值为2。

2. 使用局部变量窗口查看变量的值

除了鼠标悬停法,还可以通过V isual Studio 2019的“局部变量”窗口查看变量的值。在菜单栏中选择【调试】→【窗口】→【局部变量】,打开“局部变量”窗口查看变量的值,在“局部变量”窗口中可以看到当前运行代码之前所有变量的名称、当前值和类型,如图11-23所示。

图11-21 鼠标悬停查看变量flag的值(1)

图11-22 鼠标悬停查看变量flag的值(2)

3. 使用“快速监视”窗口查看变量的值

程序调试过程中,在代码区单击鼠标右键选择【快速监视】,弹出“快速监视”窗口,在该窗口的“表达式”文本框中输入要监视的变量,单击【重新计算】按钮,就可以查看变量的名称、值与数据类型,如图11-24所示。

图11-23 “局部变量”窗口

4. 使用“即时”窗口查看变量的值

在代码调试的过程中,在菜单栏中选择【调试】→【窗口】→【即时】打开“即时”窗口,在“即时”窗口中直接输入程序中的变量名,按Enter键即可查看变量的值,也可以在变量名前加上“&”(取地址符)查看变量的地址,如图11-25所示。

图11-24 “快速监视”窗口

图11-25 “即时窗口”

11.4.4 项目调试

在前面几节中讲解了程序调试的相关知识,为了让读者真实体验实际开发中进行程序调试的全过程,接下来以酒店管理系统为例演示程序调试过程。

在酒店管理系统的主菜单中,要求输入1~5之间的整数,如果输入不符合要求的数据,如输入一个字符,则系统会一直闪屏无响应。对此进行分析,主菜单的输入操作由界面模块的run()函数实现,在例11-6所示的room_view.cpp文件中,第81~105行代码定义int类型变量flag,利用对象cin从键盘输入flag的值,然后判断flag的值,根据flag的值调用不同的函数以实现不同的操作。

当输入字符时,系统闪屏无响应,表明第81~105行代码在输入flag值时或switch…case语句对flag的处理不够严谨,需要对此段代码进行调试。

在例11-6第81行代码设置断点,如图11-26所示。

在图11-26中,设置断点之后开始调试,通过逐语句调试跟踪每一步操作。当程序运行至第83行代码时,发现调用cin输入flag的值时,没有对cin作错误处理。利用cin为int类型变量输入数据时,如果输入的是char类型的数据,则cin的一个错误标记会被设置,cin就不能再使用,再次调用cin>>flag会直接返回false(即0)。switch…case语句无法匹配到0,因此,while(state)无限循环,导致系统主菜单界面闪屏无响应。

图11-26 在room_view.cpp第81行代码设置断点

经过上面的调试和分析,已确定了程序中的问题所在。此时,可以首先通过调用clear()函数清除cin的错误标志,恢复cin的默认状态;然后再调用ignore()函数将cin中不符合输入选项的数据忽略。

在图11-26中第83行代码后添加如下代码,解决输入错误导致程序运行出错的问题。

cin.clear();  //清除cin的错误标志 
cin.ignore();  //忽略cin中残留数据 

11.5 项目心得

在实际生活中,开发一个项目总会遇到各种各样的问题,每开发完一个项目都需要进行简单的总结,酒店管理系统也不例外,下面就总结一下酒店管理系统项目的开发心得。

1. 项目整体规划

每一个项目,在实现之前都要分析设计本项目要实现哪些功能。将这些功能划分成不同的模块,如果模块较大,还可以在内部划分成更小的功能模块。这样逐个实现每个模块,条理清晰。在分别实现各个模块后,再将这些模块整合,使各个功能协调、有序地进行。

2. 类的设计

在项目开发中,类的设计要简洁、独立。在本项目中,每个模块设计了一个类,每个类根据模块的功能封装成员变量与成员函数。GuestRoom类负责保存客房数据,GuestRoomM anager类用于管理客房数据,GuestV iew类用于显示系统界面,三个类相互独立又相互联系。

GuestRoom类是底层数据,GuestRoomM anager类是中间管理层数据,GuestView类是显示层数据,若需要增加新的功能只需要修改GuestRoomM anager类,即增加新的操作,上层显示模块GuestV iew类只需要调用GuestRoomM anager类中的成员函数即可。

在设计这三个类时,GuestRoom类将客房数据保存到文件或者从文件中读取数据时,以m ap容器作为载体,实现客房数据的存储和读取;GuestRoomM anager类以m ap容器作为成员变量,实现对客房数据的管理;GuestV iew类以GuestRoomM anager类对象作为成员变量,以实现各个操作的界面管理。

3. 清屏

酒店管理系统是一个多操作场景项目,每一次切换操作都会把上一个操作的内容清屏,如果不清屏,多次操作后会造成屏幕内容显示过多。本项目中多处使用清屏语句system("cls"),做到了良好的场景切换体验。

11.6 本章小结

本章综合运用前面所讲的知识,设计了一个综合项目——酒店管理系统,目的是帮助大家了解如何开发一个多模块、多文件的C++程序。在开发这个程序时,首先将一个项目拆分成若干个小的模块,然后分别设计每个模块所需要的类。在实现时,将每个模块的声明和定义分开,放置在头文件和源文件中,最后在一个含有m ain()函数的源文件中,将它们的头文件包含进来,并利用m ain()函数将所有的模块联系起来。通过酒店管理系统项目的学习,读者会对C++程序开发流程有个整体的认识,这对实际工作大有裨益。