C++ Primer Plus(第六版)程式設計練習答案 第12章 類和動態記憶體分配
本章所有程式設計練習的工程原始碼可在此處下載(點選此連結下載),供大家參考交流!
1. 對於下面的類宣告:
class Cow {
char name[20];
char *hobby;
double weight;
public:
Cow();
Cow(const char * nm, const char * ho, double wt);
Cow(const Cow c&);
~Cow();
Cow & operator=(const Cow & c);
void ShowCow() const; // display all new data
};
給這個類提供實現,並編寫一個使用所有成員函式的小程式。
對於本題,首先題中已經給出了類宣告,所以標頭檔案不需要自己定義,可以直接使。
所以標頭檔案cow.h程式碼如下:
// cow.h -- head file #ifndef COW_H_ #define COW_H_ class Cow { private: char name[20]; char * hobby; double weight; public: Cow(); Cow(const char * nm, const char * ho, double wt); Cow(const Cow & c); ~Cow(); Cow & operator=(const Cow & c); void ShowCow() const; }; #endif
在這裡,我們之所以在書中提供的類宣告的前後加上#ifndef, #define, #endif主要是為了防止該.h檔案被重複引用。。
聲明瞭標頭檔案之後,我們首先要考慮去定義標頭檔案中的函式宣告。
標頭檔案的私有成員變數中有字串陣列和字串指標,因此需要用到strcpy_s函式,所以標頭檔案中需要包含<cstring>;在建構函式中,唯一需要注意的就是字串指標變數需要使用new來動態分配記憶體,然後再使用strcpy_s函式進行賦值,而相應的解構函式一定要記得將建構函式中使用new動態分配的記憶體delete掉。
另外,在過載運算子=時,需要在一開始delete一下,以防止此時this指標的hobby變數儲存的地址是被分配使用過的。
所以實現檔案cow.cpp程式碼如下:
// cow.cpp -- containing the functions' definition
#include "stdafx.h"
#include <iostream>
#include <cstring>
#include "cow.h"
using std::cout;
using std::endl;
Cow::Cow()
{
strcpy_s(name, 20, "new cow");
hobby = new char[4];
strcpy_s(hobby, 4, "cow");
weight = 0.0;
}
Cow::Cow(const char * nm, const char * ho, double wt)
{
weight = wt;
hobby = new char[strlen(ho) + 1];
strcpy_s(hobby, strlen(ho)+1, ho);
strcpy_s(name, 20, nm);
}
Cow::Cow(const Cow & c)
{
strcpy_s(name, 20, c.name);
hobby = new char[strlen(c.hobby) + 1];
strcpy_s(hobby, strlen(c.hobby) + 1, c.hobby);
weight = c.weight;
}
Cow::~Cow()
{
delete[] hobby;
}
Cow & Cow::operator=(const Cow & c)
{
delete[] hobby;
hobby = new char[strlen(c.hobby) + 1];
strcpy_s(hobby, strlen(c.hobby) + 1, c.hobby);
weight = c.weight;
strcpy_s(name, 20, c.name);
return *this;
}
void Cow::ShowCow() const
{
cout << "Now a new cow!\n";
cout << "The name is " << name << endl;
cout << "The hobby is " << hobby << endl;
cout << "The weight is " << weight << endl;
}
接下來我們來考慮如何使用這些成員函式組成檢驗檔案。
其實本題的檔案的主要功能只有3個,各種方式的函式初始化,使用過載的運算子=,使用ShowCow函式進行顯示。
檢驗檔案checkcow.cpp程式碼如下:
// checkcow.cpp -- check all the functions
#include "stdafx.h"
#include <iostream>
#include "cow.h"
using namespace std;
int main()
{
cout << "Let's start to see our cows!\n";
Cow c1;
c1.ShowCow();
Cow c2("yellow", "eat grass", 123.456);
c2.ShowCow();
Cow c3("black", "drink water", 222.333);
c3.ShowCow();
c3 = c1;
Cow c4(c2);
c3.ShowCow();
c4.ShowCow();
system("pause");
return 0;
}
執行結果如下圖所示:
2. 通過完成下面的工作來改進String類宣告(即將String1.h升級為String2.h)。
a. 對+運算子進行過載,使之可將兩個字串合併成1個。
b. 提供一個Stringlow()成員函式,將字串中所有的字母字元轉換為小寫(別忘了cctype系列字元函式)。
c. 提供String()成員函式,將字串中所有字母字元轉換成大寫。
d. 提供一個這樣的成員函式,它接受一個char引數,返回該字元在字串中出現的次數。
使用下面的程式來測試您的工作:
(……程式碼忽略……)
輸出應與下面相似:
(……程式碼忽略……)
本題是在String1.h的基礎上進行升級,新增幾項功能再測試。
首先,需要過載+運算子,將兩個字串合併成1個,這裡看起來只需要新增一個成員函式,其實不然。由於檢驗檔案中既有將string物件相加,又有將字串指標相加,還存在將string物件和字串指標相加的情況,所以我們需要對應的定義3個函式來分別過載這三種情況下的+運算子。
其次,提供兩個函式將字串中的所有字母字元轉換為小寫或大寫,這個功能使用<cctype>中的函式將非常輕鬆。
最後,提供一個成員函式,接受一個char引數,返回該引數在字串中出現的次數,該函式將需要遍歷整個字串進行計數。
對於標頭檔案,我們只需要新增以上6個函式即可,所以標頭檔案string2.h程式碼如下:
// string2.h -- fixed and augmented string class definition
#ifndef STRING2_H_
#define STRING2_H_
#include <iostream>
using std::ostream;
using std::istream;
class String
{
private:
char * str;
int len;
static int num_strings;
static const int CINLIM = 80;
public:
String(const char * s);
String();
String(const String &);
~String();
int length() const { return len; }
void stringlow();
void stringup();
int has(char x);
String & operator=(const String &);
String & operator=(const char *);
char & operator[] (int i);
const char & operator[] (int i) const;
friend bool operator<(const String &st1, const String &st2);
friend bool operator>(const String &st1, const String &st2);
friend bool operator==(const String &st, const String &st2);
friend ostream & operator<<(ostream & os, const String &st);
friend istream & operator>>(istream & is, String & st);
static int HowMany();
String operator+(const String &) const;
String operator+(const char *) const;
friend String operator+(const char *, const String &);
};
對於實現檔案,其他的功能可以沿用string1.cpp,新新增的幾個函式需要自定義。
首先,對於過載+運算子,我們分為三個函式。第一個是兩個string物件之間的加法,在這裡呢,我們可以使用strcat_s函式,直接將兩個string物件進行拼接,但是由於預設情況下string物件的最後一個字元都是'\0',所以處理起來比較麻煩,我選擇依然使用strcpy_s函式,只是複製的地址不一樣,比如說str1的長度為10,str2的長度為10,我們新定義一個str3,要想把str1的內容和str2的內容加起來放進str3中,就把str1的內容從str3的第一個字元處開始賦值,把str2的內容從str3的第11個字元處開始賦值,這樣就可以了。對於字串指標變數的情況,可以完全類似處理。對於字串指標變數和string物件相加的情況,我們可以人為地進行強制型別轉換,將字串指標變數轉換為string物件,再加,那麼此時相加就會呼叫前面定義的兩個string物件相加的函式,即可以完成任務。
其次,我們在實現檔案中包含標頭檔案<cstring>,然後定義stringow()函式和stringup()函式,分別來對字串的字母字元進行小寫或大寫的轉換。在轉換的時候,我們需要首先對整個字串進行遍歷,然後使用tolower()函式和toupper()函式來進行大小寫轉換,注意這裡this是指標,指向某一個變數時要使用->符號。
最後,我們需要定義has()函式。該函式遍歷整個字串,然後識別輸入的特定char型別變數,然後計數。我們在for迴圈內部巢狀一個if條件判斷即可。
實現檔案string2.cpp程式碼如下所示:
// string2.cpp -- String class methods
//
#include "stdafx.h"
#include <cstring>
#include "string2.h"
#include <cctype>
using std::cin;
using std::cout;
int String::num_strings = 0;
int String::HowMany()
{
return num_strings;
}
String::String(const char * s)
{
len = std::strlen(s);
str = new char[len + 1];
strcpy_s(str, std::strlen(s) + 1, s);
num_strings++;
}
String::String()
{
len = 4;
str = new char[1];
str[0] = '\0';
num_strings++;
}
String::String(const String & st)
{
num_strings++;
len = st.len;
str = new char[len + 1];
strcpy_s(str, std::strlen(st.str) + 1, st.str);
}
String::~String()
{
--num_strings;
delete[] str;
}
void String::stringlow()
{
for (int i = 0; i < this->len + 1; i++)
{
this->str[i] = tolower(this->str[i]);
}
}
void String::stringup()
{
for (int i = 0; i < this->len + 1; i++)
{
this->str[i] = toupper(this->str[i]);
}
}
int String::has(char x)
{
int count = 0;
for (int i = 0; i < this->len + 1; i++)
{
if (this->str[i] == x)
count++;
}
return count;
}
String & String::operator=(const String & st)
{
if (this == &st)
return *this;
delete[] str;
len = st.len;
str = new char[len + 1];
strcpy_s(str, std::strlen(st.str) + 1, st.str);
return *this;
}
String & String::operator=(const char * s)
{
delete[] str;
len = std::strlen(s);
str = new char[len + 1];
strcpy_s(str, std::strlen(s) + 1, s);
return *this;
}
char & String::operator[] (int i)
{
return str[i];
}
const char & String::operator[] (int i) const
{
return str[i];
}
bool operator<(const String &st1, const String &st2)
{
return (std::strcmp(st1.str, st2.str) < 0);
}
bool operator>(const String &st1, const String &st2)
{
return st2 < st1;
}
bool operator==(const String &st1, const String &st2)
{
return (std::strcmp(st1.str, st2.str) == 0);
}
ostream & operator<<(ostream & os, const String & st)
{
os << st.str;
return os;
}
istream & operator>>(istream & is, String & st)
{
char temp[String::CINLIM];
is.get(temp, String::CINLIM);
if (is)
st = temp;
while (is &&is.get() != '\n')
continue;
return is;
}
String String::operator+(const String & st) const
{
int total_len = len + st.len;
char *temp = new char[total_len + 1];
strcpy_s(temp, len + 1, str);
strcpy_s(temp + len, st.len + 1, st.str);
temp[total_len] = '\0';
return String(temp);
}
String String::operator+(const char * s) const
{
int total_len = len + strlen(s);
char *temp = new char[total_len + 1];
strcpy_s(temp, len + 1, str);
strcpy_s(temp + len, std::strlen(s) + 1,s);
temp[total_len] = '\0';
return String(temp);
}
String operator+(const char * s, const String & st)
{
return String(s) + st;
}
檢驗檔案題中直接給出,saying2.cpp程式碼如下所示:
// saying1.cpp -- using expanded String class
#include "stdafx.h"
#include <iostream>
#include "string2.h"
using namespace std;
int main()
{
String s1(" and I am a C++ student.");
String s2 = "Please enter your name: ";
String s3;
cout << s2;
cin >> s3;
s2 = "My name is " + s3;
cout << s2 << ".\n";
s2 = s2 + s1;
s2.stringup();
cout << "The string\n" << s2 << "\ncontains " << s2.has('A') << " 'A' characters in it.\n";
s1 = "red";
String rgb[3] = { String(s1), String("green"), String("blue") };
cout << "Enter the name of a primary color for mixing light: ";
String ans;
bool success = false;
while (cin >> ans)
{
ans.stringlow();
for (int i = 0; i < 3; i++)
{
if (ans == rgb[i])
{
cout << "That's right!\n";
success = true;
break;
}
}
if (success)
break;
else
cout << "Try again!\n";
}
cout << "Bye\n";
system("pause");
return 0;
}
執行結果如下圖所示:
從執行結果可以看出,該檢驗檔案完成的功能和預期完全一致。
3. 新編寫程式清單10.7和程式清單10.8描述的Stock類,使之使用動態記憶體分配的記憶體,而不是string類物件來儲存股票名稱。另外,使用過載的operator<<()定義代替show()成員函式。再使用程式清單10.9測試新的定義程式。
本題要求將Stock類進行修改,使用動態記憶體分配代替string類物件,所以string物件company必須改為char *型別,然後去掉show()函式,改為使用過載的operator<<()來進行輸出,該函式我們選擇使用友元。
所以標頭檔案stock20.h程式碼如下:
// stock20.h -- augmented version
#ifndef STOCK20_H_
#define STOCK20_H_
#include <iostream>
using std::ostream;
class Stock
{
private:
char * company;
int shares;
double share_val;
double total_val;
void set_tot() { total_val = shares * share_val; }
public:
Stock();
Stock(const char * co, long n = 0, double pr = 0.0);
~Stock();
void buy(long num, double price);
void sell(long num, double price);
void update(double price);
friend ostream & operator<<(ostream & os, const Stock &st);
const Stock & topval(const Stock & s) const;
};
#endif
對於實現檔案,當company變數從string物件變成char *型別之後,建構函式那裡就必須要改變,必須使用new來動態分配它的記憶體,而且解構函式必須要delete。對於過載的<<運算子,我們也按照原來的show()函式一樣的格式,對ios_base裡面的處理不變,只是在此之後,按照標準的過載<<運算子的方式進行輸出,同時將cout完全換成我們的輸出os。
實現檔案stock20.cpp程式碼如下:
// stock20.cpp -- augnented version
#include "stdafx.h"
#include <iostream>
#include "stock20.h"
using std::ostream;
Stock::Stock()
{
company = new char[std::strlen("no name") + 1];
strcpy_s(company, std::strlen("no name") + 1, "no name");
shares = 0;
share_val = 0.0;
total_val = 0.0;
}
Stock::Stock(const char * co, long n, double pr)
{
company = new char[std::strlen(co) + 1];
strcpy_s(company, std::strlen(co) + 1, co);
if (n < 0)
{
std::cout << "Number of shares can't be negative; " << company << " shares set to 0.\n";
shares = 0;
}
else
shares = n;
share_val = pr;
set_tot();
}
Stock::~Stock()
{
delete[] company;
}
void Stock::buy(long num, double price)
{
if (num < 0)
{
std::cout << "Number of shares purchased can't be negative. " << "Transaction is aborted.\n";
}
else
{
shares += num;
share_val = price;
set_tot();
}
}
void Stock::sell(long num, double price)
{
using std::cout;
if (num < 0)
{
cout << "Number of shares sold can't be negative. " << "Transaction is aborted.\n";
}
else if (num > shares)
{
cout << "You can't sell more than you have! " << "Transaction is aborted.\n";
}
else
{
shares -= num;
share_val = price;
set_tot();
}
}
void Stock::update(double price)
{
share_val = price;
set_tot();
}
ostream & operator<<(ostream & os, const Stock &st)
{
using std::ios_base;
ios_base::fmtflags orig = os.setf(ios_base::fixed, ios_base::floatfield);
std::streamsize prec = os.precision(3);
os << "Company: " << st.company << " Shares: " <<st.shares << "\n";
os << "Share Price: $" << st.share_val;
os.precision(2);
os << " Total Worth: $" << st.total_val << "\n";
os.setf(orig, ios_base::floatfield);
os.precision(prec);
return os;
}
const Stock & Stock::topval(const Stock & s) const
{
if (s.total_val > total_val)
return s;
else
return *this;
}
檢驗檔案題目要求直接使用程式清單10.9,所以usestock20.cpp程式碼如下:
// usestock20.cpp -- using the Stock class
// compile with stock20.cpp
#include "stdafx.h"
#include <iostream>
#include "stock20.h"
using std::cout;
const int STKS = 4;
int main()
{
Stock stocks[STKS] =
{
Stock("NanoSmart", 12, 20.0),
Stock("Boffo Objects", 200, 2.0),
Stock("Monolithic Obelisks", 130, 3.25),
Stock("Fleep Enterprises", 60, 6.5)
};
std::cout << "Stock holdings:\n";
int st;
for (st = 0; st < STKS; st++)
cout << stocks[st];
const Stock * top = &stocks[0];
for (st = 1; st < STKS; st++)
top = &top->topval(stocks[st]);
cout << "\nMost valuable holding:\n";
cout << *top;
system("pause");
return 0;
}
執行結果如下圖所示:
4. 請看下面程式清單10.10定義的Stack類的變數:
(……程式碼忽略……)
正如私有成員表明的,這個類使用動態記憶體分配的陣列來儲存棧項。請重新編寫方法,以適應這種新的表示法,並編寫一個程式來演示所有的方法,包括複製建構函式和賦值運算子。
本題提供了標頭檔案程式碼,需要注意的地方在於,本題的私有成員中,定義了一個無符號long型別的指標,對於這種型別的變數,我們同樣需要使用new來動態分配記憶體。
標頭檔案stack.h程式碼如下:
// stack.h -- class declaration for the stack ADT
#include <iostream>
using std::ostream;
typedef unsigned long Item;
class Stack
{
private:
enum { MAX = 10 };
Item * pitems;
int size;
int top;
public:
Stack(int n = MAX);
Stack(const Stack & st);
~Stack();
bool isempty() const;
bool isfull() const;
bool push(const Item & item);
bool pop(Item & item);
Stack & operator=(const Stack & st);
friend ostream & operator<<(ostream & os, const Stack &st);
};
對於實現檔案,大部分內容我們可以參考程式清單10.11,而新增的size變數和修改過的pitems物件,我們需要單獨編寫,且要注意pitems物件必須使用new來動態分配記憶體,解構函式裡面必須將其delete,另外在賦值運算子進行過載時,必須首先將pitems物件進行delete,以防止它的記憶體已經被佔用。
另外我們需要額外編寫賦值建構函式,因為要防止初始化時預設建構函式產生二義性;還需要過載<<運算子用來輸出。
實現檔案stack.cpp程式碼如下:
// stack.cpp -- functions' definition
#include "stdafx.h"
#include <iostream>
using std::endl;
#include "stack.h"
Stack::Stack(int n)
{
pitems = new Item[n];
for (int i = 0; i < n; i++)
{
pitems[i] = 0;
}
top = 0;
size = n;
}
Stack::Stack(const Stack & st)
{
pitems = new Item[st.size];
for (int i = 0; i < st.size; i++)
{
pitems[i] = st.pitems[i];
}
top = st.top;
size = st.size;
}
Stack::~Stack()
{
delete[] pitems;
}
bool Stack::isempty() const
{
return top == 0;
}
bool Stack::isfull() const
{
return top == MAX;
}
bool Stack::push(const Item & item)
{
if (top < MAX)
{
pitems[top++] = item;
return true;
}
else
return false;
}
bool Stack::pop(Item & item)
{
if (top > 0)
{
item = pitems[--top];
return true;
}
else
return false;
}
Stack & Stack::operator=(const Stack & st)
{
if (this == &st)
return *this;
delete[] pitems;
top = st.top;
size = st.size;
pitems = new Item[st.size];
for (int i = 0; i < st.size; i++)
{
pitems[i] = st.pitems[i];
}
return *this;
}
ostream & operator<<(ostream & os, const Stack & st)
{
for (int i = 0; i < st.top; i++)
{
os << st.pitems[i] << endl;
}
return os;
}
接下來我們可以在程式清單10.12的基礎上進行少量修改,就可以檢驗上面編寫的類方法了。對於檢驗棧是否為空和是否為滿,以及入棧出棧,我們都可以完全參考程式清單10.12,下面我們主要檢驗新的功能,包括使用複製建構函式進行賦值,使用過載的=運算子和<<運算子。
檢驗檔案usestack.cpp程式碼如下:
// usestack.cpp -- tetsing the Stack class
#include "stdafx.h"
#include <iostream>
#include <cctype>
#include "stack.h"
int main()
{
using namespace std;
Stack st;
char ch;
unsigned long po;
cout << "Please enter A to add a purchase order,\n" << "P to process a PO, Q to quit.\n";
while (cin >> ch && toupper(ch) != 'Q')
{
while (cin.get() != '\n')
continue;
if (!isalpha(ch))
{
cout << '\a';
continue;
}
switch (ch)
{
case 'A':
case 'a': cout << "Enter a PO number to add: ";
cin >> po;
if (st.isfull())
cout << "stack already full.\n";
else
st.push(po);
break;
case 'p':
case 'P': if (st.isempty())
cout << "stack already empty.\n";
else
{
st.pop(po);
cout << "PO #" << po << " popped.\n";
}
break;
}
cout << "Please enter A to add a purchase order,\n" << "P to process a PO, or Q to quit.\n";
}
cout << "So our stack is:\n" << st;
Stack st2;
st2 = st;
cout << "stack2 = stack is:\n" << st2;
cout << "Bye\n";
system("pause");
return 0;
}
執行結果如下圖所示:
從結果圖可以看出,我們首先檢驗了一下程式清單10.12檢驗的功能,都可以正確執行,接下來我們對棧內資料進行了填充,然後進行了複製建構函式式賦值,以及=運算子賦值,都是正確的。
5. Heather銀行進行的研究表明,ATM客戶不希望排隊時間不超過1分鐘。使用程式清單12.10中的模擬,找出要使平均等候時間為1分鐘,每小時到達的客戶數應為多少(試驗時間不短於100小時)?
首先,我個人認為本題的所謂不希望排隊時間不超過1分鐘是寫錯了,應該是不希望排隊時間超過1分鐘,後面的程式碼也都是基於這個假設寫的。
其次,由於本題涉及的功能完全是程式清單12.10標頭檔案和程式清單12.11實現檔案可以完成的,所以標頭檔案我們不需要修改,直接使用即可。
所以標頭檔案queue.h程式碼如下:
// queue.h -- interface for a queue
#ifndef QUEUE_H_
#define QUEUE_H_
class Customer
{
private:
long arrive;
int processtime;
public:
Customer()
{
arrive = processtime = 0;
}
void set(long when);
long when() const
{
return arrive;
}
int ptime() const
{
return processtime;
}
};
typedef Customer Item;
class Queue
{
private:
struct Node
{
Item item;
struct Node * next;
};
enum { Q_SIZE = 10 };
Node * front;
Node * rear;
int items;
const int qsize;
Queue(const Queue & q) : qsize(0) { }
Queue & operator=(const Queue & q)
{
return *this;
}
public:
Queue(int qs = Q_SIZE);
~Queue();
bool isempty() const;
bool isfull() const;
int queuecount() const;
bool enqueue(const Item &item);
bool dequeue(Item &item);
};
#endif
實現檔案queue.cpp程式碼如下:
// queue.cpp -- Queue and Customer methods
#include "stdafx.h"
#include "queue.h"
#include <cstdlib>
Queue::Queue(int qs) : qsize(qs)
{
front = rear = NULL;
items = 0;
}
Queue::~Queue()
{
Node * temp;
while (front != NULL)
{
temp = front;
front = front->next;
delete temp;
}
}
bool Queue::isempty() const
{
return items == 0;
}
bool Queue::isfull() const
{
return items == qsize;
}
int Queue::queuecount() const
{
return items;
}
bool Queue::enqueue(const Item &item)
{
if (isfull())
return false;
Node * add = new Node;
add->item = item;
add->next = NULL;
items++;
if (front == NULL)
front = add;
else
rear->next = add;
rear = add;
return true;
}
bool Queue::dequeue(Item &item)
{
if (front == NULL)
return false;
item = front->item;
items--;
Node * temp = front;
front = front->next;
delete temp;
if (items == 0)
rear = NULL;
return true;
}
void Customer::set(long when)
{
processtime = std::rand() % 3 + 1;
arrive = when;
}
接下來主要需要修改的就是檢驗檔案了。
首先呢,我們這裡假設試驗時間不短於100小時,為了簡單起見,我就將試驗時間定為100小時,接下來我們初始化perhour為1,再對perhour變數進行累加迴圈,即對每小時到達的客戶數量進行累加迴圈;定義一個avetime變數,表示平均等候時間;使用while語句,判斷條件為(perhour++ && avetime <= 1),即從每小時到達客戶數為1開始,依次增加每小時到達的客戶數,同時滿足平均等候時間不超過1分鐘,以此作為迴圈條件。再在迴圈內部去進行for迴圈操作。
這裡需要注意的是,在for迴圈之前,我們需要先清空佇列,這樣才是真正的從完全為空開始進行試驗,不然無法控制初始條件下佇列中原來就存在的客戶數量。
在for迴圈的最後,我們需要計算一下avetime,即將line_wait除以served。
具體詳情請閱讀程式碼,檢驗檔案bank.cpp程式碼如下:
// bank.cpp -- using the Queue interface
// compile with queue.cpp
#include "stdafx.h"
#include <iostream>
#include <cstdlib>
#include <ctime>
#include "queue.h"
const int MIN_PER_HR = 60;
bool newcustomer(double x);
int main()
{
using std::cin;
using std::cout;
using std::endl;
using std::ios_base;
std::srand(std::time(0));
cout << "Case Study: Bank of Heather Automatic Teller\n";
cout << "Enter maximum size of queue: ";
int qs;
cin >> qs;
Queue line(qs);
cout << "The simulation hours: 100\n";
int hours = 100;
long cyclelimit = MIN_PER_HR * hours;
double perhour;
double min_per_cust;
perhour = 1;
Item temp;
long turnaways = 0;
long customers = 0;
long served = 0;
long sum_line = 0;
int wait_time = 0;
long line_wait = 0;
double avetime = 0;
while (perhour++ && avetime <= 1)
{
while (!line.isempty())
{
line.dequeue(temp);
}
min_per_cust = MIN_PER_HR / perhour;
for (int cycle = 0; cycle < cyclelimit; cycle++)
{
if (newcustomer(min_per_cust))
{
if (line.isfull())
turnaways++;
else
{
customers++;
temp.set(cycle);
line.enqueue(temp);
}
}
if (wait_time <= 0 && !line.isempty())
{
line.dequeue(temp);
wait_time = temp.ptime();
line_wait += cycle - temp.when();
served++;
}
if (wait_time > 0)
wait_time--;
sum_line += line.queuecount();
}
if (customers > 0)
{
cout << "customers accepted: " << customers << endl;
cout << " customers served: " << served << endl;
cout << " turnaways: " << turnaways << 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";
avetime = (double)line_wait / served;
}
cout << "When there comes " << perhour << " people per hour, the average wait time will be about 1 minute.\n";
cout << "Done!\n";
system("pause");
return 0;
}
bool newcustomer(double x)
{
return (std::rand() * x / RAND_MAX < 1);
}
執行結果如下圖所示:
在這裡我輸入的佇列最大客戶數是10,由於需要輸出100組資料,所以我只截了開頭和結尾,無法全部顯示中間的細節結果。從最後的結果上來看,根據100小時的實驗,當每小時到達客戶數為25.00時,排隊時間剛好不超過1分鐘。
6. Heather銀行想知道,如果再開設一臺ATM,情況將如何。請對模擬進行修改,以包含兩個佇列。假設當第一臺ATM前的排隊人數少於第二臺ATM時,客戶將排在第一隊,否則將排在第二隊。然後再找出要使平均等候時間為1分鐘,每小時到達的客戶數應該為多少(注意,這是一個非線性問題,即將ATM數量加倍,並不能保證每小時處理的客戶數量也翻倍,並確保客戶等候的時間少於1分鐘)?
本題要求多開設一個ATM,所以我們就會有兩個佇列,一起進行服務,客戶到達之後的排隊方式即是,如果第一臺人數少於第二臺,則排第一隊,否則排第二隊。所以在這裡,標頭檔案和實現檔案依然不變,因為本質性功能依然沒變。
所以標頭檔案queue.h程式碼如下:
// queue.h -- interface for a queue
#ifndef QUEUE_H_
#define QUEUE_H_
class Customer
{
private:
long arrive;
int processtime;
public:
Customer()
{
arrive = processtime = 0;
}
void set(long when);
long when() const
{
return arrive;
}
int ptime() const
{
return processtime;
}
};
typedef Customer Item;
class Queue
{
private:
struct Node
{
Item item;
struct Node * next;
};
enum { Q_SIZE = 10 };
Node * front;
Node * rear;
int items;
const int qsize;
Queue(const Queue & q) : qsize(0) { }
Queue & operator=(const Queue & q)
{
return *this;
}
public:
Queue(int qs = Q_SIZE);
~Queue();
bool isempty() const;
bool isfull() const;
int queuecount() const;
bool enqueue(const Item &item);
bool dequeue(Item &item);
};
#endif
實現檔案queue.cpp程式碼如下:
// queue.cpp -- Queue and Customer methods
#include "stdafx.h"
#include "queue.h"
#include <cstdlib>
Queue::Queue(int qs) : qsize(qs)
{
front = rear = NULL;
items = 0;
}
Queue::~Queue()
{
Node * temp;
while (front != NULL)
{
temp = front;
front = front->next;
delete temp;
}
}
bool Queue::isempty() const
{
return items == 0;
}
bool Queue::isfull() const
{
return items == qsize;
}
int Queue::queuecount() const
{
return items;
}
bool Queue::enqueue(const Item &item)
{
if (isfull())
return false;
Node * add = new Node;
add->item = item;
add->next = NULL;
items++;
if (front == NULL)
front = add;
else
rear->next = add;
rear = add;
return true;
}
bool Queue::dequeue(Item &item)
{
if (front == NULL)
return false;
item = front->item;
items--;
Node * temp = front;
front = front->next;
delete temp;
if (items == 0)
rear = NULL;
return true;
}
void Customer::set(long when)
{
processtime = std::rand() % 3 + 1;
arrive = when;
}
接下來我們著重修改檢驗檔案。
首先,本題有兩臺ATM,所以我們需要兩個Queue物件,分別稱為line1和line2,接下來的處理方法和上一題類似,我們假設試驗時間為100小時,初始化perhour為1,然後自定義avetime,對perhour進行累加迴圈,同時判斷avetime <= 1;但是這裡需要注意,我們現在是兩個ATM同時服務,所以到達的客戶需要有一個選擇,即排哪個隊,根據題目要求,我們肯定需要有兩個line_size和wait_time,所以定義wait_time1和wait_time2分別表示佇列1和佇列2的等候時間,定義line1_size和line2_size分別表示佇列1和佇列2的尺寸。
在迴圈內部,我們首先依然還是要清空佇列,這裡就要清空兩次了;然後在for迴圈內部,當有客戶到達時,判斷是否turnaway就需要去判斷兩個隊列了;如果不滿足turnaway的條件,就比較line1_size和line2_size,來決定到達的客戶排哪個佇列;後面判斷出隊和計算sum_line,注意都需要進行兩次。
其他操作和上題相同,注意有些需要操作兩次即可。
具體詳情請閱讀程式碼,檢驗檔案bank.cpp程式碼如下:
// bank.cpp -- using the Queue interface
// compile with queue.cpp
#include "stdafx.h"
#include <iostream>
#include <cstdlib>
#include <ctime>
#include "queue.h"
const int MIN_PER_HR = 60;
bool newcustomer(double x);
int main()
{
using std::cin;
using std::cout;
using std::endl;
using std::ios_base;
std::srand(std::time(0));
cout << "Case Study: Bank of Heather Automatic Teller\n";
cout << "Enter maximum size of queue: ";
int qs;
cin >> qs;
Queue line1(qs);
Queue line2(qs);
cout << "The simulation hours: 100\n";
int hours = 100;
long cyclelimit = MIN_PER_HR * hours;
double perhour;
double min_per_cust;
perhour = 1;
Item temp;
long turnaways = 0;
long customers = 0;
long served = 0;
long sum_line = 0;
int wait_time1 = 0;
int wait_time2 = 0;
int line1_size = 0;
int line2_size = 0;
long line_wait = 0;
double avetime = 0;
while (perhour++ && avetime <= 1)
{
while (!line1.isempty())
{
line1.dequeue(temp);
}
while (!line2.isempty())
{
line2.dequeue(temp);
}
min_per_cust = MIN_PER_HR / perhour;
for (int cycle = 0; cycle < cyclelimit; cycle++)
{
if (newcustomer(min_per_cust))
{
if (line1.isfull() && line2.isfull())
turnaways++;
else if(line1_size < line2_size)
{
customers++;
temp.set(cycle);
line1.enqueue(temp);
line1_size++;
}
else
{
customers++;
temp.set(cycle);
line2.enqueue(temp);
line2_size++;
}
}
if (wait_time1 <= 0 && !line1.isempty())
{
line1.dequeue(temp);
line1_size--;
wait_time1 = temp.ptime();
line_wait += cycle - temp.when();
served++;
}
if (wait_time2 <= 0 && !line2.isempty())
{
line2.dequeue(temp);
line2_size--;
wait_time2 = temp.ptime();
line_wait += cycle - temp.when();
served++;
}
if (wait_time1 > 0)
wait_time1--;
if (wait_time2 > 0)
wait_time2--;
sum_line += line1.queuecount();
sum_line += line2.queuecount();
}
if (customers > 0)
{
cout << "customers accepted: " << customers << endl;
cout << " customers served: " << served << endl;
cout << " turnaways: " << turnaways << 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";
avetime = (double)line_wait / served;
}
cout << "When there comes " << perhour << " people per hour, the average wait time will be about 1 minute.\n";
cout << "Done!\n";
system("pause");
return 0;
}
bool newcustomer(double x)
{
return (std::rand() * x / RAND_MAX < 1);
}
執行結果如下圖所示:
從結果上可以看出,當只有1臺ATM時,每小時到達客戶數為25時,平均等候時間為1分鐘,而兩臺ATM時,每小時到達客戶數為60,平均等候時間為1分鐘,所以效果不止翻倍。