目录
12.1
// class.h
#pragma once
#ifndef _CLASS_H_
#define _CLASS_H_
class Cow
{
private:
char name[20];
char* hobby; //需要使用new来分配内存
double weight;
public:
Cow();
Cow(const char* nm, const char* ho, double wt);
Cow(const Cow& c); //复制构造函数
~Cow(); //需要使用与构造函数中new配套的delete
Cow* operator=(const Cow& c);
void ShowCow()const;
};
#endif
// fuc.cpp
#pragma warning(disable:4996)
#include<iostream>
#include<cstring> // strcpy(a,b) strncpy(a,b,length)
#include"class.h"
Cow::Cow()
{
strncpy(name, "none",4);
hobby = nullptr;
weight = 0;
}
Cow::Cow(const char* nm, const char* ho, double wt)
{
strncpy(name, nm,20);
//为private中的char*分配空间并赋值:
int charlength = strlen(ho); //获取长度
hobby = new char[charlength + 1]; // 为hobby分配内存空间,数组大小为strlen获取的参数字符串长度+1
strncpy(hobby, ho, charlength + 1);
weight = wt;
}
Cow::Cow(const Cow& c) //复制构造函数
{
strncpy(name, c.name, 20);
//深度复制:分配内存后再复制值
int ho_length = strlen(c.hobby);
hobby = new char[ho_length + 1];
strncpy(hobby, c.hobby, ho_length + 1);
weight = c.weight;
}
Cow::~Cow() //需要使用与构造函数中new配套的delete
{
delete[] hobby;
hobby = nullptr;
}
Cow* Cow::operator=(const Cow& c)
{
Cow temp;
strncpy(temp.name, c.name, 20);
delete[]hobby;//防止内存的浪费,赋值前先清空此部分占用的内存
//深度赋值
int ho_length = strlen(c.hobby);
hobby = new char[ho_length + 1];
strncpy(temp.hobby, c.hobby, ho_length + 1);
temp.weight = c.weight;
return temp
}
void Cow::ShowCow()const
{
std::cout << "name: " << name << std::endl;
std::cout << "hobby: " << hobby << std::endl;
std::cout << "weight: " << weight << std::endl;
}
// main.cpp
#include"class.h"
#include<iostream>
int main()
{
Cow c1("GodFishhh", "Video games", 80);
Cow c2("AFish", "Basketball", 70);
//运用复制构造函数(参数为类对象的构造函数)
Cow c3(c1);
//运用重载赋值运算符
Cow c4 = c2;
c1.ShowCow();
c2.ShowCow();
c3.ShowCow();
c4.ShowCow();
system("pause");
return 0;
}
知识点:
1.cstring头文件提供了两个用于字符串复制的函数,分别为strcpy(a,b)和strncpy(a,b,length),strcpy接受两个参数,将字符串b复制给a(不是很安全),strncpy接受三个参数,其第三个参数设置了最多可以复制的字符数(若b的字符数小于length,剩余的字符将用空字符来填充)。
2.当类中有指向char类型的指针时,多半要在复制构造函数和重载赋值运算符中用到深度复制 -- 即赋值值而不是指针。
流程:1).通过strlen()获取复制文本字符串的长度。2).通过new分配内存,其中数组的长度应当为[length+1]。3).通过strncpy(a,b,length+1)实现最后的复制
eg:
复制构造函数:
Cow::Cow(const Cow& c) //复制构造函数
{
strncpy(name, c.name, 20);
//深度复制:分配内存后再复制值
int ho_length = strlen(c.hobby);
hobby = new char[ho_length + 1];
strncpy(hobby, c.hobby, ho_length + 1);
weight = c.weight;
}
重载赋值运算符:
Cow* Cow::operator=(const Cow& c)
{
Cow temp;
strncpy(temp.name, c.name, 20);
delete[]hobby;//防止内存的浪费,赋值前先清空此部分占用的内存
//深度复制
int ho_length = strlen(c.hobby);
hobby = new char[ho_length + 1];
strncpy(temp.hobby, c.hobby, ho_length + 1);
temp.weight = c.weight;
return temp;
}
ps: 重载赋值运算符中相较于复制构造函数多了一行delete的代码,是因为复制构造函数多用于初始化新的对象,所以之前没有为其中的数据分配过内存,但赋值的对象则可能在此前就已经分配了内存,所以在深度复制之前进行一次delete,防止内存的浪费。
3.解决strcpy和strncpy不安全无法使用:
1):在使用该函数文件的最顶部加上:#pragma warning(disable:4996)
2):在项目-属性-配置属性-c/c++-预处理器-预处理定义里边加上一句: _CRT_SECURE_NO_WARNINGS
12.2
// class.h
#pragma once
#ifndef _CLASS_H_
#define _CLASS_H_
#include<iostream>
using std::istream;
using std::ostream;
class String
{
private:
char* str; //字符串指针,此处没有分配内存,后续用new来为其开辟内存空间
int len;
static int num_strings; //只有static,则必须声明后在实现文件中进行初始化
static const int CINLIM = 80; //const static 可以直接在类中初始化
public:
String(const char* s);
String();
String(const String& s);//赋值构造函数
~String(); //在使用new的类中十分重要 -- 配套使用delete来释放堆空间
int length() const
{
return len;
};
String& operator=(const String& st); //对象赋值
String& operator=(const char* s); //一般字符串赋值
//运算符重载
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& st1, const String& st2);
friend istream& operator>>(istream& is, String& st); // cin>>
friend ostream& operator<<(ostream& os, const String& st); // cout<<
//重载 + 运算符
friend String& operator+(const char* c1, const String& s1);//普通字符串+对象字符串
friend String& operator+(const String& s1, const char* c1);//对象字符串+普通字符串
friend String& operator+(const String& s1, const String& s2);//对象字符串+对象字符串
//Stringlow()函数,将字符串中的大写转换成小写 ;A:65 a:97 (ASCII码值相差32)
void Stringlow();
//Stringhigh()函数,将字符串中的小写转换为大写
void Stringup();
//提供一个这样的成员函数,它接受一个char参数,返回该字符在字符串中出现的次数、
int has(char c);
//静态函数
static int HowMany();
};
#endif
// fuc.cpp
#pragma warning(disable:4996); //使得strcpy可以正常使用
#include<cstring> // strcpy--复制字符串, strcmp--根据ASCII码值比较字符串,strlen--获取字符串的长度
#include<cctype> //isalpha()判断是否为字母,islower()判断是否为是否为小写字母,isupper()判断是否为大写字母
#include"class.h"
using namespace std;
//为类中声明的static变量初始化,且要声明其所在的类区域
int String::num_strings = 0;
int String::HowMany()
{
return num_strings;
}
String::String(const char* s)
{
len = std::strlen(s); //strlen获取的是字符串除去末尾空字符的长度,所以长度要为 len+1;
str = new char[len + 1]; //在构造函数中使用new申请堆空间,在析构函数中使用delete释放堆空间
strcpy(str, s);
num_strings++;
}
String::String()
{
len = 4;
str = new char[len + 1];
str[0] = '\0'; //设置为空指针,也可为设置为 NULL, nullptr,(void *)0q
num_strings++;
}
String::String(const String& s)//复制构造函数
{
num_strings++; //为新初始化的对象复制,所以可以在总数上加上1
len = s.len;
//深度复制:
str = new char[len + 1];
strcpy(str, s.str);
}
String::~String() //在使用new的类中十分重要 -- 配套使用delete来释放堆空间
{
--num_strings;
delete[] str;
}
String& String::operator=(const String& st) //对象赋值
{
if (this == &st) //如果调用对象和参数已经相同
{
return *this;
}
delete[] str; //先删除之前的内存,防止内存的浪费
len = st.len;
//深度复制
str = new char[len + 1];
strcpy(str, st.str);
return *this; //使得可以进行连续复制操作
}
String& String::operator=(const char* s) //一般字符串赋值
{
delete[] str;
len = strlen(s);
str = new char[len + 1];
strcpy(str, s);
return *this;
}
char& String::operator[](int i) //重载 中括号[] 运算符 //可改变数据版本
{
return str[i]; //即返回调用对象字符串的第i个字符
}
const char& String::operator[](int i)const//不改变数据版本
{
return str[i];
}
//友元函数:
bool operator<(const String& st1, const String& st2)
{
return (strcmp(st1.str, st2.str) < 0);
}
bool operator>(const String& st1, const String& st2)
{
return st2 < st1; //直接使用上面刚重载的运算符 逻辑:若语句为: st1>st2,若成立则应当有st2<st1,可以实现函数功能
//或 return (strcmp(st1.str,st2.str)>0)
}
bool operator==(const String& st1, const String& st2)
{
return (strcmp(st1.str, st2.str) == 0);
}
istream& operator>>(istream& is, String& st) // cin>>
{
char temp[String::CINLIM]; //友元函数,不能直接使用类中的静态常量,需要使用作用域解析运算符
is.get(temp, String::CINLIM); // 相当于cin,get(name,size)
if (is)
{
st = temp; //使用的重载过的对于常规字符串的赋值运算符
}
//while的作用:读取掉剩下的多余出来的字符
while (is && is.get() != '\n') // 若输入失败,则istream对象is的值将被置为false;
{
continue;
}
return is;
}
ostream& operator<<(ostream& os, const String& st) // cout<<
{
os << st.str;
return os;
}
//重载 + 运算符 ( strcat(a,b)实现将b字符串加在a字符串的后面)
String& operator+(const char* c1, const String& s1)//普通字符串+对象字符串
{
String temp;
int te_length = strlen(c1) + strlen(s1.str);
temp.str = new char[te_length + 1];
//先将c1放在temp.str后面,再将s1.str放在更新过的temp.str后
strcat(temp.str, c1);
strcat(temp.str, s1.str);
return temp;
}
String& operator+(const String& s1, const char* c1)//对象字符串+普通字符串
{
String temp;
int te_length = strlen(c1) + strlen(s1.str);
temp.str = new char[te_length + 1];
strcat(temp.str, s1.str);
strcat(temp.str, c1);
return temp;
}
String& operator+(const String& s1, const String& s2)//对象字符串+对象字符串
{
String temp;
int te_length = strlen(s1.str) + strlen(s2.str);
temp.str = new char[te_length + 1];
strcat(temp.str, s1.str);
strcat(temp.str, s2.str);
return temp;
}
void String::Stringlow()
{
int str_length = strlen(str); //strlen获取的字符串长度是不包括空字符的,所以根据这个值来创建字符串数组经常要加上1 -- str_length + 1
for (int i = 0; i < str_length+1; i++)
{
if (isalpha(str[i]))
{
if (isupper(str[i]))
{
str[i] += 32; //将大写字母转换为小写字母
}
}
}
}
void String::Stringup()
{
int str_length = strlen(str);
for (int i = 0; i < str_length; i++)
{
if (isalpha(str[i]))
{
if (islower(str[i]))
{
str[i] -= 32; //将小写字母转换成大写字母
}
}
}
}
int String::has(char c)
{
int str_length = strlen(str);
int counts = 0;
for (int i = 0; i < str_length + 1; i++)
{
if (str[i] == c)
{
counts++;
}
}
return counts;
}
// main.cpp
//要求:
//1.对+运算符进行重载,使之可以将两个字符串合并成一个
//2.提供一个Stringlow()成员函数,将字符串中所有的字母字符转换为小写 -- cctype中的isalpha A(65) -- a(97) 大小写的ASCII码值相差32
//3.提供String()成员函数,将字符串中所有的字母字符转换成大写
//4.提供一个这样的成员函数,它接受一个char参数,返回该字符在字符串中出现的次数
#include<iostream>
#include"class.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;
}
知识点:
1.strcpy(a,b):将字符串b复制给字符串a strcat(a,b):将字符串b放在字符串a的后面
2.大写字母与小写字母的ASCII码值相差32,且小写字母的ASCII码值比大写字母的更大
eg: A -- 65 a -- 97
3.头文件cctype中常用的函数:
1):isalpha():判断一个字符是否为字母
2):isalnum():判断一个字符是否为数字或字母
3):islower():判断一个字符是否为小写字母
4):toupper():若字符为小写字母,则将其转换为大写字母 -- 相当于ASCII码值 - 32
5):tolower():若字符为大写字母,则将其转换为小写字母 -- 相当于ASCII码值 + 32
6):isdigit():判断一个字符是否为数字
7):to_string():将数字转换为字符串
12.3
#pragma once
// class.h
#ifndef _CLASS_H_
#define _CLASS_H_
#include<string>
using namespace std;
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 std::string &name = NAME, int sh =0,double sh_v=0)
Stock(const char*name, int sh = 0, double sh_v = 0);
~Stock();//析构函数
void buy(long num, double price);
void sell(long num, double price);
void update(double price);
/*void show()const;*///此const可以确保该函数访问的隐性数据不发生改变
const Stock& topval(const Stock& s) const;
//上面的topval中含有三个const
// 第一个const是使得返回值的类型为const的Stock类的引用对象
// 第二个const是保证引用的Stock对象不发生改变
// 第三个const是确保此函数访问的隐性数据不发生改变
//重载 << 运算符
friend ostream& operator << (ostream & os, const Stock & s1);
// ostream流没有公用的复制构造函数,所以返回类型和参数都必须是引用,用值传递将会用到副本,即会用到复制构造函数
};
#endif
// fuc.cpp
#pragma warning(disable:4996);
#include<iostream>
#include"class.h"
Stock::Stock() //默认构造函数
{
int com_length = strlen("no name");
company = new char[com_length + 1];
strcpy(company, "no name");
shares = 0;
share_val = 0;
set_tot();
}
Stock::Stock(const char*name, int sh, double sh_v) //默认参数只需要写在声明中,而在函数定义中不能加上默认参数
{
int com_length = strlen(name);
company = new char[com_length + 1];
strcpy(company, name);
if (sh < 0)
{
cout << "shares can not under 0" << endl;
cout << "You have no shares in " << name << endl;
}
else
{
shares = sh;
share_val = sh_v;
set_tot();
}
}
Stock::~Stock()
{
delete[] company; //与构造函数用用new创建数组对应
cout << "the end of the object" << endl;
}
void Stock::buy(long num, double price)
{
if (num < 0)
{
cout << "The number is negative" << endl;
}
else
{
shares += num;
share_val = price;
set_tot();
}
}
void Stock::sell(long num, double price)
{
if (num < 0)
{
cout << "The number is negative" << endl;
}
else
{
shares -= num;
share_val = price;
set_tot();
}
}
void Stock::update(double price)
{
share_val = price;
set_tot();
}
//void Stock::show()const
//{
// cout << "Company: " << company << endl;
// cout << "Shares: " << shares << endl;
// cout << "Share_value: " << share_val << endl;
// cout << "Total_value: " << total_val << endl;
//}
const Stock& Stock::topval(const Stock& s) const // const Stock&为返回类型,而Stock::加在函数名前
{
if (s.total_val > total_val)
{
return s;
}
else if (s.total_val < this->total_val) //this是指向调用对象的指针: 所以 this->total_val 等价于 total_val
{
return *this; // this是指针,则*this就相当于该调用对象的一个引用
}
}
//重载 << 运算符
ostream& operator << (ostream& os, const Stock& s1)
{
os << "company: " << s1.company << endl;
os << "shars: " << s1.shares << endl;
os << "share_value: " << s1.share_val << endl;
os << "total_value: " << s1.total_val << endl;
return os;
}
//构造函数: 与类名相同 eg: 类名Stock 则 构造函数名为Stock
// 同时,若程序员没有自己设置构造函数,则系统会自动设置默认构造函数,且不进行初始化
// 若程序员自己设置了构造函数,则必须同时自己设置一个默认构造函数,可以通过默认参数或者函数重载来设置默认构造函数
// 初始化的显式方法和隐式方法: 显式: Stock stock1 = Stock( , , ,) 隐式: Stock stock2( , , ,) -- 初始化过程相当于调用函数过程
//析构函数: 在类名前加上一个~ eg: 类名Stock 则 析构函数名为~Stock
// 析构函数在类的对象内存被回收时,发挥作用
// this指针:指针指向调用对象 eg: Stock stock 则 stock.show() (直接访问) 与 this -> show() (通过地址间接访问)的语义相同
// this是一个指针,则 *this 则相当于调用对象的一个引用
// 对象数组: 与结构体数组类似
// eg: Stock stock[] -- 初始化时要通过显式方法: Stock{ , , ,}
// main.cpp
//修改目标:
//1.使用动态分配的内存来取代string类对象来储存股票名字
//2.使用重载的operator<<()定义来代替show()成员函数
#include<iostream>
#include"class.h"
int main()
{
//创建对象数组,并用自己创建的构造函数来进行初始化
Stock stocks[4] =
{
Stock("GodFishhh",100,200),
Stock("AFish",200,80),
Stock("Boffo",20,50),
Stock("Fleep",30,60),
};
cout << "Shares holding: " << endl;
int i;
for (i = 0; i < 4; i++)
{
cout<<stocks[i];
cout << "---------------------" << endl;
}
//找出total_val最高的对象
const Stock* top = &stocks[0]; //将top设为对象数组的首地址,以便后续进行比较
for (i = 1; i < 4; i++)
{
top = &top->topval(stocks[i]);
//逻辑: top是一个对象的地址,所以通过top->访问topval函数,并传入参数stocks[i],同时函数的返回值为Stock对象引用,所以要取其地址再赋值给top
}
cout << "The top value information: " << endl;
//top->show();
cout << *top; //top是指向对象的指针,则*top就是对象本身
system("pause");
return 0;
}
知识点:
1.获取char字符串的长度用函数strlen(), 获取string字符串的长度用stringname.length()
12.4
// class.h
#pragma once
#ifndef _CLASS_H_
#define _CLASS_H_
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(); //定义中必须要有与new想配对的delete函数
bool isempty()const;
bool isfull()const;
bool push(const Item& item);
bool pop(Item& item); //参数不用const是因为该函数要用参数保存被pop出的数据,再于main函数中输出该数据
Stack& operator=(const Stack& st); //重载赋值运算符
};
#endif
// fuc.cpp
#include"class.h"
#include<iostream>
using namespace std;
Stack::Stack(int n) //默认参数只需写在声明中
{
size = n;
top = 0;
pitems = new Item[size];
}
Stack::Stack(const Stack& st)//复制构造函数
{
size = st.size;
top = st.top;
//深度复制: 先分配内存空间再赋值
pitems = new Item[top];
pitems = st.pitems;
}
Stack::~Stack() //定义中必须要有与new相配对的delete函数
{
delete[]pitems;
}
bool Stack::isempty()const
{
if (top == 0) //指向标top在最初的位置,则为empty
{
return true;
}
else
{
return false;
}
}
bool Stack::isfull()const
{
if (top == size) //指向标top在最高的位置,则为full
{
return true;
}
else
{
return false;
}
}
bool Stack::push(const Item& item)
{
if (isfull())
{
cout << "The stack is already full" << endl;
return false;
}
else
{
//逻辑:此时stack的top处为空,所以先给此top处的栈区赋值再自增,即后增
pitems[top++] = item;
return true;
}
}
bool Stack::pop(Item& item) //参数不用const是因为该函数要用参数保存被pop出的数据,再于main函数中输出该数据
{
if (isempty())
{
cout << "The stack is already empty" << endl;
return false;
}
else
{
//逻辑:top所处的栈空间没有值,--top移到上一位将要pop出从值,且用参数item保存下该值以便后续在main函数中显示
item = pitems[--top];
return true;
}
}
Stack& Stack::operator=(const Stack& st) //重载赋值运算符
{
delete[]pitems; //防止内存的浪费
size = st.size;
top = st.top;
pitems = new Item[size];
pitems = st.pitems;
return *this; // *this就是调用对象本身
}
// main.cpp
#include<iostream>
#include"class.h"
using namespace std;
int main()
{
Stack s1;
Item num;
char ch;
char code;
int counts=0;
cout << "Please enter single E to enter the program" << endl;
while (cin>>code&&code=='E')
{
cout << "Please enter the process you wanna perform" << endl;
cout << "A -- push the number your enter " << endl;
cout << "P -- pop a number out" << endl;
cin >> ch;
switch(ch)
{
case 'a':
case 'A':
{
if (s1.isfull())
{
cout << "The stack is already full" << endl;
}
else
{
cout << "Please enter a number to push into the Stack" << endl;
cin >> num;
s1.push(num);
counts++;
}
break;
}
case 'p':
case 'P':
{
if (s1.isempty())
{
cout << "The stack is already empty" << endl;
}
else
{
Item temp1;
s1.pop(temp1); //传递的参数为num的引用,所以形参的改变会影响实参
cout << "The number you have poped out is: " << temp1 << endl;
counts--;
}
break;
}
default:
cout << "Please enter the right code" << endl;
}
cout << "Please enter a single E to enter the program again or 'q' to quit" << endl;
}
Stack s2 = s1;
cout << "Pop s2: " << endl;
for (int i = 0; i < counts; i++)
{
Item temp2;
s2.pop(temp2);
cout << temp2 << " has been poped out" << endl;
}
system("pause");
return 0;
}
知识点:
1.重载成员赋值运算符时,返回值是一个类对象的引用,此时有两种选择:第一种是创建一个临时对象,然后将参数的各项值赋值给临时对象,最后再返回这个临时对象的引用。第二种则是直接将参数的各项值赋值给调用对象,最后返回*this即可。(*this就是调用对象本身,this是指向调用对象的指针)
12.5
// queue.h
#pragma once
#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; // Customer与Item等价
class Queue
{
private:
struct Node
{
Item item; //顾客数据
Node* next; //创建指向结构体的指针来形成链表
};
enum { Q_size = 10 }; // 等价于 const static Q_size = 10;
Node* front;
Node* rear;
int items;
const int qsize; // const对象 -- 通过成员初始化来赋值/创建的时候直接初始化 (不可以在构造函数中赋值)
// 将复制构造函数和重载赋值运算符放在private空间中,因为该程序不会用到这两个功能,但又要防止意外用到这两个功能遭成不必要的错误
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
#include<cstdlib>
#include"queue.h"
// class: Queue
Queue::Queue(int qs) :qsize(qs) //将qs赋值给const对象qsize -- 成员初始化
{
front = rear = NULL; // nullptr,(void *)0 均可
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; // 向后添加,最后一位的后面为NULL
items++;
if (front == NULL) //如果是第一个
{
front = add;
}
else
{
rear->next = add; //当前最后一位的下一位为add,即新添加的成员
}
rear = add; //将rear标记至最后一位,方便后续的添加
return true;
}
bool Queue::dequeue(Item& item) // dequeue的主要逻辑为: 用一个临时指针保存front的地址,再更新front值front->next即下一位,最后再delete临时指针指向的那块内存即可完成dequeue
{
if (front == NULL)
{
return false;
}
item = front->item; //参数的作用为获取被dequeue的成员的信息
items--;
Node* temp = front;
front = front->next;
delete temp;
if (items == 0)
{
rear = NULL;
}
return true;
}
// class Customer
void Customer::set(long when)
{
processtime = std::rand() % 3 + 1;
arrive = when;
}
// bank.cpp
// 目标要求:找出要使平均等候时间为1分钟,每小时到达的客户数应为多少(试验时间不短于100小时)
// 实现方法: 通过一个循环:do---while(average_time>1),然后对perhour不断地进行迭代(perhour的值越小,average_time越小0
#include<iostream>
#include<cstdlib> // RAND_MAX 和 RAND_MIN 分别为rand()可以返回的最大值和最小值
#include<ctime>
#include"queue.h"
const int MIN_PER_HR = 60;
using namespace std;
bool newcustomer(double x);
int main()
{
srand(time(0));
int flag = 1;
//队列允许同时存在的最大人数设置
cout << "Case Study: Bank of Heather Automatic Teller" << endl;
cout << "Enter maximum size of queue:";
int qs;
cin >> qs;
Queue line(qs);
//总共的模拟时间设置
int hours = 100; //实验时间不小于100h(直接设置为100小时)
long cyclelimit = MIN_PER_HR * hours;
//每小时平均有多少顾客设置
double perhour=20; //初始值设置为20,再进行迭代
double min_per_cust;
while (flag)
{
//每次的perhour值不同,就相当于一个新的计算方式,则所有的记录数据都要清空重新计算
Item temp;
long turnaways = 0;
long customers = 0;
long served = 0;
long sum_line = 0;
int wait_time = 0;
long line_wait = 0;
min_per_cust = MIN_PER_HR / perhour; //每次更新perhour,min_per_cust也要同步放在while循环中进行更新
for (int cycle = 0; cycle < cyclelimit; cycle++) // cycle用于标记位置
{
if (newcustomer(min_per_cust)) //如果有新顾客
{
if (line.isfull()) //如果当前队列已满
{
turnaways++;
}
else //且队列并不满
{
//进行enqueue过程
customers++;
temp.set(cycle); //set的作用为设置当前顾客是第几分钟到达的且通过rand随机数来设置该顾客所需要的processtime
line.enqueue(temp);
}
}
if (wait_time <= 0 && !line.isempty()) //如果等待时间小于0且队列并不空
{
//进行dequeue过程 (被dequeue的那个对象被送去办理,应当保存其processtime -- wait_time)
line.dequeue(temp); //确认将要dequeue的对象为temp,则temp被送去进行办理
wait_time = temp.ptime(); //同时temp办理所需要的时间保存至wait_time
line_wait += cycle - temp.when(); //所处的队列位置为当前的位置减去顾客进入时的位置
served++;
}
if (wait_time > 0) //当wait_time仍大于0时,说明temp仍在办理,wait_time--
{
wait_time--;
}
sum_line += line.queuecount();
}
//reporting:
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" << endl;
}
else
{
cout << "No customers!" << endl;
}
cout << "Done!" << endl;
if ((double)line_wait / served < 1)
{
flag = 0;
}
else
{
cout << "The average wait_time is more than one minutes" << endl;
perhour--;
}
}
cout << "While the average wait_time is less than one minues, customers perhous are: " << perhour << endl;
system("pause");
return 0;
}
bool newcustomer(double x)
{
return (rand() * x / RAND_MAX < 1);
}
12.6
// queue.h
#pragma once
#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; // Customer与Item等价
class Queue
{
private:
struct Node
{
Item item; //顾客数据
Node* next; //创建指向结构体的指针来形成链表
};
enum { Q_size = 10 }; // 等价于 const static Q_size = 10;
Node* front;
Node* rear;
int items;
const int qsize; // const对象 -- 通过成员初始化来赋值/创建的时候直接初始化 (不可以在构造函数中赋值)
// 将复制构造函数和重载赋值运算符放在private空间中,因为该程序不会用到这两个功能,但又要防止意外用到这两个功能遭成不必要的错误
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
#include<cstdlib>
#include"queue.h"
// class: Queue
Queue::Queue(int qs) :qsize(qs) //将qs赋值给const对象qsize -- 成员初始化
{
front = rear = NULL; // nullptr,(void *)0 均可
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; // 向后添加,最后一位的后面为NULL
items++;
if (front == NULL) //如果是第一个
{
front = add;
}
else
{
rear->next = add; //当前最后一位的下一位为add,即新添加的成员
}
rear = add; //将rear标记至最后一位,方便后续的添加
return true;
}
bool Queue::dequeue(Item& item) // dequeue的主要逻辑为: 用一个临时指针保存front的地址,再更新front值front->next即下一位,最后再delete临时指针指向的那块内存即可完成dequeue
{
if (front == NULL)
{
return false;
}
item = front->item; //参数的作用为获取被dequeue的成员的信息
items--;
Node* temp = front;
front = front->next;
delete temp;
if (items == 0)
{
rear = NULL;
}
return true;
}
// class Customer
void Customer::set(long when)
{
processtime = std::rand() % 3 + 1;
arrive = when;
}
// bank.cpp
//修改要求:
//再开设一台ATM(两个队列),若第一台ATM机的排队人数少于第二台,则客户将排到第一台,否则应排到第二台
//最后再找出平均等待时间小于1min时,每小时的到达的客户应该是多少
#include<iostream>
#include<cstdlib> // RAND_MAX 和 RAND_MIN 分别为rand()可以返回的最大值和最小值
#include<ctime>
#include"queue.h"
const int MIN_PER_HR = 60;
using namespace std;
bool newcustomer(double x);
int main()
{
srand(time(0));
//队列允许同时存在的最大人数设置
cout << "Case Study: Bank of Heather Automatic Teller" << endl;
cout << "Enter maximum size of queue: ";
int qs;
cin >> qs;
//两台ATM机
Queue line1(qs);
Queue line2(qs);
int flag = 1;
//总共的模拟时间设置
cout << "Enter the number of simulations hours: ";
int hours = 100;
long cyclelimit = MIN_PER_HR * hours;
//每小时平均有多少顾客设置
cout << "Enter the average number of customers per hour: ";
double perhour = 20; //perhour直接从20开始迭代
double min_per_cust;
//成员函数queuecount返回当前队列中的人数
while (flag)
{
//更新记录数据
Item temp;
long turnaways = 0;
long customers = 0;
long served = 0;
long sum_line = 0;
int wait_time = 0;
long line_wait = 0;
//更新min_per_cust
min_per_cust = MIN_PER_HR / perhour;
for (int cycle = 0; cycle < cyclelimit; cycle++) // cycle用于标记位置
{
if (newcustomer(min_per_cust)) //如果有新顾客
{
if (line1.isfull()&&line2.isfull()) //如果当前队列已满
{
turnaways++;
}
else //且队列并不满
{
//进行enqueue过程
if (line1.queuecount() <= line2.queuecount())
{
customers++;
temp.set(cycle); //set的作用为设置当前顾客是第几分钟到达的且通过rand随机数来设置该顾客所需要的processtime
line1.enqueue(temp);
}
else
{
customers++;
temp.set(cycle);
line2.enqueue(temp);
}
}
}
if (wait_time <= 0 && !line1.isempty()&&!line2.isempty()) //如果等待时间小于0且队列并不空
{
if (line1.queuecount() >= line2.queuecount())
{
//进行dequeue过程 (被dequeue的那个对象被送去办理,应当保存其processtime -- wait_time)
line1.dequeue(temp); //确认将要dequeue的对象为temp,则temp被送去进行办理
wait_time = temp.ptime(); //同时temp办理所需要的时间保存至wait_time
line_wait += cycle - temp.when(); //所处的队列位置为当前的位置减去顾客进入时的位置
served++;
}
else
{
//进行dequeue过程 (被dequeue的那个对象被送去办理,应当保存其processtime -- wait_time)
line2.dequeue(temp); //确认将要dequeue的对象为temp,则temp被送去进行办理
wait_time = temp.ptime(); //同时temp办理所需要的时间保存至wait_time
line_wait += cycle - temp.when(); //所处的队列位置为当前的位置减去顾客进入时的位置
served++;
}
}
if (wait_time > 0) //当wait_time仍大于0时,说明temp仍在办理,wait_time--
{
wait_time--;
}
sum_line += line1.queuecount();
sum_line += line2.queuecount();
}
//reporting:
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" << endl;
}
else
{
cout << "No customers!" << endl;
}
cout << "Done!" << endl;
//目标二:
if ((double)line_wait / served < 1)
{
cout << "Average wait_time right now is under 1 minutes with " << perhour << " customers in one hour" << endl;
flag = 0;
}
else
{
cout << "Average wait_time is still above 1 mintues" << endl;
perhour--;
}
}
system("pause");
return 0;
}
bool newcustomer(double x)
{
return (rand() * x / RAND_MAX < 1);
}