ATM 模拟来自C++ Primer Plus
Queue.h
#ifndef QUEUE_H
#define QUEUE_H
#include<iostream>
//模板函数、模板类
//新发现,单独的模板函数不能分.h.cpp
template<class T>
class FQueue
{
private:
struct Node
{
T item;
struct Node* next;
};
// T& a = item;//ok
// 引用可以内部,外部列表初始化
// T& a; //无意义,试引用列表初始化
// static const int qshi;//无意义,只为测试类外初始化static模板类时
enum {Qsize = 10};//10后面无分号
Node * front; //首元素
Node * rear; //尾元素
int items;
const int qsize;
//这是一个编程技巧,下面定义伪私有方法,为保证不小心调用=等,程序不会崩溃 PlusP468
FQueue(const FQueue & q):qsize(0){}
FQueue& operator =(const FQueue & q) {return this;}
//定义了这两个方法后,若再在类外不小心调用了队列的对象a = b则会报错
//eg: FQueue snick(nip);tuck = nip;都会编译出错,另外还要注意
//返回值是要返回引用,否则也将隐蔽的调用复制构造函数,所以用类对象时不要轻易返回对象主返回引用
public:
FQueue(int qs);
~FQueue();
bool isempty() const;
bool isfull() const;
int queuecount() const;
bool enqueue(const T &item);
bool dequeue(T& item); //这里本来不用传参,只是这里返回了T/F,所以用引用的方式返回值
};
//template<class T > const int FQueue<T>::qshi = 10;
//这里又一个知识点,static 有总结,C++11标准单独const可以类内初始化,单独static不可以
//二者连起来也可以,但是这里的点是类外初始化时的格式,注意无static关键字
// 如果不是模板类则const int FQueue::qsize = 10
template<class T>
//FQueue<T>::FQueue(int qs):qsize(qs),qshi(10)
//多个时用逗号相隔,另外验证这种初始化不能用于静态常量
//FQueue<T>::FQueue(int qs):qsize(qs),a(items) //试试引用列表初始化
FQueue<T>::FQueue(int qs):qsize(qs)
//这里又一个知识点,:qsize(qs)是初始化,qsize = qs是赋值,当qsize是const时
//区别就出来了,当变量类型是常量(非静态const)或引用时,必须采用这种初始化方法 Primer Plus P464
//有点出入,因为C++11已经支持非静态const类内初始化,所以也不是必须
{
front = nullptr;
rear = nullptr;
items = 0;
//qsize = qs;//给参数提供默认值
}
template<class T>
FQueue<T>::~FQueue() //class名字后面别忘了加<T>
{
//保证对象消亡时,依次删除各个节点(因为是new产生)
//这是一种很好的删除方式
Node * temp;
while(nullptr != front)
{
temp = front;
front = front->next;
delete temp;
}
}
template<class T>
bool FQueue<T>::isempty() const
{
return 0 == items; //高效,别if了,总忘
}
template<class T>
bool FQueue<T>::isfull() const
{
return items == qsize;
}
template<class T>
int FQueue<T>::queuecount() const
{
return items;
}
template<class T>
bool FQueue<T>::enqueue(const T &item)
{
if (isfull())
return false;
Node * add = new Node;
add->item = item;
add->next = nullptr;
items++;
if (nullptr == front) //测试时崩溃了,又是因为这种==写成了=
front = add;
else
rear->next = add;
rear = add;
return true;
}
template<class T>
bool FQueue<T>::dequeue(T& item)
{
if (nullptr == front)
return false;
item = front->item;
Node * temp = front; //这里多这一步是为了删除已经出队列的节点
front = front->next;
delete temp; //为节省内存,删除已经出列的节点,这是以前自己写一直想做的,不只是指示变量移位,也达到了真正删除
items--;
if (0 == items)
rear = nullptr;
return true;
}
#endif // QUEUE_H
Customer.h
#ifndef CUSTOMER_H
#define CUSTOMER_H
#include<cstdlib> //rand in it
class Customer
{
private:
long arrive; //arrival time for customer
int processtime;// processing time for customer
public:
Customer(){arrive = processtime = 0;}
void set(long when);
long when() const {return arrive;}
int ptime() const{return processtime;}
};
void Customer::set(long when)
{
processtime = std::rand() % 3 + 1;
// 设置1到3 的随机数,Plus P461介绍的,顾客获得服务的时间为1~3分钟
arrive = when;
}
#endif // CUSTOMER_H
Main.cpp
#include <iostream>
#include<cstdlib> //for rand and srand
#include<ctime> // for time
#include"queue.h"
#include"customer.h"
bool newcustomer(double x); //if there is a new customer
const int MIN_PER_HR = 60;
//试继承类public下,基类的私有成员成为派生类的一部分,但只能通过基类的公有和保护方法访问
//class shiji : public Customer
//{
// public:
// void show(){std::cout << arrive ;} //错误
//};
int main()
{
using namespace std;
srand(time(0)); //random initializing of rand()
cout << "Case Study: Bank of Fan Automatic Teller" << endl;
cout << "Please enter maximum size of queue: ";
int qs;
//检查输入
while (!( cin >> qs) )
{
cout << endl << "Bad input! Please input again : " ;
cin.clear();
cin.ignore(256,'\n');// 为什么要Ignore,因为cin虽然可以自动忽略空格回车符等
//但是若输入不成功,字符被放回输入流
// cin.get(); //单个错误字符输入也可用这个处理掉,但是若输入多个字符则
//判断违法输入也可终止程序,需cstdlib,然后退出是exit(EXIT_FAILURE);
//()内是给系统发出的信号,EXIT_DAILURE宏定义为1
}
FQueue<Customer> line(qs);
cout << "Please input the number of simulation hours : ";
int hours;
cin >> hours; //上面已经演示如何检查输入了,这里为了简洁就不再了
long cyclelimit = MIN_PER_HR * hours;
cout << "Please input the average number of customer per hour :";
double perhour;
cin >> perhour;
double min_per_cust;
min_per_cust = MIN_PER_HR / perhour;
Customer temp; //new customer data
long turnaways = 0; //turned away(拒绝) by full queue
long customers = 0;//
long served =0;
long sum_line = 0;
int wait_time = 0;
long line_wait = 0;
for(int cycle = 0; cycle < cyclelimit ; cycle++) //每次循环代表一分钟
{
if(newcustomer(min_per_cust)) //have newcomer
{
if(line.isfull())
turnaways++;
else
{
customers++;
temp.set(cycle); //cycle = time of arrival(这里是Int,而when是long,这long是不是多占空间了)
line.enqueue(temp);
}
}
if (wait_time <= 0 && !line.isempty())
{
line.dequeue(temp);
wait_time = temp.ptime();
//获得服务所需时间,实际就是该用户用多久ATM
line_wait += cycle - temp.when();
//在队列中等待的时间
served ++;
}
if (wait_time > 0)
wait_time--;
sum_line += line.queuecount();
//队列长度
}
//show results
if (customers > 0)
{
cout << "customers accepted : " << customers << endl;
cout << " customers serverd :" << served << endl;
cout << " turnaways: " << turnaways << endl;
cout << "average queue size:";
cout.precision(2); //控制精度,定点模式fixed和科学技术下,精度指小数点后尾数,默认指总位数
cout.setf(ios_base::fixed,ios_base::floatfield); //控制输出格式第一第二参数的形式Plus P749
cout << (double)sum_line /cyclelimit <<endl;
cout << " average wait time: " << (double) line_wait/served << " minutes" << endl;
}
else
cout << "No customers!" << endl;
cout << " bye!" << endl;
cin.clear(); //清楚标志位
while (cin.get() != '\n')
continue; //清楚输入流数据
return 0;
}
bool newcustomer(double x)
{
return (std::rand()*x/RAND_MAX < 1);
}
//定义依次循环一分钟,这里平均六分钟来一个客户,所以一分钟来客户的概率是1/6
//rand/RAND_MAX 返回0~1 ,乘以x,就是返回0~x,这里返回0~6,<1的概率1/6