注意,每小时到达的客户从15名增加到30名时,等候时间并不是加倍,而是增加了15倍。如果允许队列更长,情况将更糟。然而,模拟没有考虑到这个事实–许多客户由于不愿意排很长的队而离开了。下面是该程序的另外几个运行示例。从中可知,即使平均每小时到达的客户数不变,也会出现短期变化。
#pragma region 12.12.bank.cpp
/*
bank.cpp -- using the Queue interface
//complic with queue.cpp
*/
#if 0
#include <iostream>
#include<cstdlib>
#include<ctime>
#include"queue.h"
const int MIN_PER_HR = 60;
bool newcustomer(double x);//is there a new customer?
int main()
{
using std::cin;
using std::cout;
using std::endl;
using std::ios_base;
//setting things up
std::srand(std::time(0));//random initializing of rand();
cout << "案例研究:希瑟银行自动柜员\n";
cout << "输入队列的最大大小: ";
int qs;
cin >> qs;
Queue line(qs); //line queue holds up to qs people
cout << "输入求和小时数:";
int hours;
cin >> hours;
//simulation while run 1 cycle per minute
//每分钟运行 1 个周期时进行模拟
long cyclelimit = MIN_PER_HR * hours;//# fof cycles
cout << "输入每小时的平均客户数: ";
double perhour; //average# if arrival per hour平均# 如果每小时到达
cin >> perhour;
double min_per_cust; //average time between arrivals平均到达间隔时间
min_per_cust = MIN_PER_HR / perhour;
Item temp; //new customer data,模拟一个新的顾客
long turnawary = 0; //turned away by full queue 被排满的队伍拒之门外
long customers = 0; //joined the queue 加入了队列
long served = 0; //served during the simulation模拟的服务时间
long sum_line = 0; //cumulative line length 累加步长
int wait_time = 0; //time until autoteller is free 自动柜员机空闲前的时间
long line_wait = 0; //cumulative time in line 在线累加时长
//running the simulation
for (int cycle = 0; cycle < cyclelimit; cycle++)
{
if (newcustomer(min_per_cust))//have newcomer 有新人
{
if (line.isfull())
{
turnawary++;
}
else
{
customers++;
temp.set(cycle); //cycle = time of arrival
line.enqueue(temp);//add newcome to line
}
}
//bool booltemp = line.isempty();
if (wait_time <= 0 && !line.isempty())
{
line.dequeue(temp); //attend next customer
wait_time = temp.ptime(); //for wait_time minutes
line_wait += cycle - temp.when();
served++;
}
if (wait_time > 0)
wait_time--;
sum_line += line.queuecount();
}
//reporting results
if (customers > 0)
{
cout << "custoners accepted: " << customers << endl;
cout << " customers served: " << served << endl;
cout << " turnaways: " << turnawary << endl;
cout << "average queue size: ";
cout.precision(2);
cout.setf(ios_base::fixed, ios_base::floatfield);
cout << (double)sum_line / cyclelimit << endl;
cout << "average wait time: " << (double)line_wait / served << " minutes\n";
}
else
{
cout << "No customers!\n";
}
return 0;
}
bool newcustomer(double x)
{
return (std::rand() * x / RAND_MAX < 1);
}
#endif
#pragma endregion
总结12.8
本章介绍了定义和使用类的许多重要方面。其中的一些方面是非常微妙甚至很难理解的概念。如果其中的某些概念对于您来说过于复杂,也不用害怕–这些问题对于大多数 C++的初学者来说都是很难的。通常,对于诸如复制构造函数等概念,都是在由于忽略它们而遇到了麻烦后逐步理解的。本章介绍的一些内容乍看起来非常难以理解,但是随着经验越来越丰富,对其理解也将越透彻。
在类构造函数中,可以使用 new 为数据分配内存,然后将内存地址赋给类成员。这样,类便可以处理长度不同的字符串,而不用在类设计时提前固定数组的长度。在类构造函数中使用new,也可能在对象过期时引发问题。如果对象包含成员指针,同时它指向的内存是由new分配的,则释放用于保存对象的内存并不会自动释放对象成员指针指向的内存。因此在类构造函数中使用new类来分配内存时,应在类析构函数中使用 delete 来释放分配的内存。这样,当对象过期时,将自动释放其指针成员指向的内存。
如果对象包含指向 new 分配的内存的指针成员,则将一个对象初始化为另一个对象,或将一个对象赋给另一个对象时,也会出现问题。在默认情况下,C++逐个对成员进行初始化和赋值,这意味着被初始化或被赋值的对象的成员将与原始对象完全相同。如果原始对象的成员指向一个数据块,则副本成员将指向同一个数据块。当程序最终删除这两个对象时,类的析构函数将试图删除同一个内存数据块两次,这将出错。解决方法是:定义一个特殊的复制构造函数来重新定义初始化,并重载赋值运算符。在上述任何一种情况下,新的定义都将创建指向数据的副本,并使新对象指向这些副本。这样,旧对象和新对象都将引用独立的、相同的数据,而不会重叠。由于同样的原因,必须定义赋值运算符。对于每一种情况,最终目的都是执行深度复制,也就是说,复制实际的数据,而不仅仅是复制指向数据的指针。
对象的存储持续性为自动或外部时,在它不再存在时将自动调用其析构函数。如果使用new 运算符为对象分配内存,并将其地址赋给一个指针,则当您将 delete用于该指针时将自动为对象调用析构函数。然而,如果使用定位 new 运算符(而不是常规new 运算符)为类对象分配内存,则必须负责显式地为该对象调用析构函数,方法是使用指向该对象的指针调用析构函数方法。
C++允许在类中包含结构、类和举定义。这些嵌套类型的作用域为整个类,这意味着它们被局限于类中,不会与其他地方定义的同名结构、类和枚举发生冲突。C++为类构造函数提供了一种可用来初始化数据成员的特殊语法。这种语法包括冒号和由逗号分隔的初始化列表,被放在构造函数参数的右括号后,函数体的左括号之前。每一个初始化器都由被初始化的成员的名称和包含初始值的括号组成。从概念上来说,这些初始化操作是在对象创建时进行的,此时函数体
中的语句还没有执行。语法如下:
queue(int gs):qsize(qs),items(0),front(NULL),rear(NULL)
如果数据成员是非静态 const成员或引用,则必须采用这种格式,但可将 C++11 新增的类内初始化用于非静态 const 成员。
C++11允许类内初始化,即在类定义中进行初始化:
class Queue
private:
{
Node *front=NULL;
enum(QSIZE=10);
Node *rear=UL;
int items =0;
const int qsize=QSIZE;
}
这与使用成员初始化列表等价。然而,使用成员初始化列表的构造函数将覆盖相应的类内初始化。您可能已经注意到,与简单的C结构相比,需要注意的类细节要多得多。作为回报,它们的功能也更强