C++ Primer Plus 第十二章习题答案+收获

目录

12.1

12.2

12.3

12.4

12.5

12.6


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);
}

评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值