1.Wine类有一个string类对象成员(参见第4章)和一个Pair对象(参见本章);其中前者用于存储葡萄酒的名称,而后者有2个valarray<int>
对象(参见本章),这两个valarray<int>
对象可能为1988、1992和1996年,第二个valarray<int>
对象可能为24、48和144瓶。Wine最好有1个int成员用于存储年数。另外,一些typedef可能有助于简化编程工作:
typedef std::valarray<int> ArrayInt;
typedef Pair<ArrayInt, ArrayInt> PairArray;
这样,PairArray表示的是类型Pairstd::valarray<int>
,std::valarray<int>
。使用包含来实现Wine类,并用一个简单的程序对其进行测试。Wine类应该有一个默认构造函数以及如下构造函数:
// initialize label to l, number of years to y,
// vintage years to yr[], bottles to bot[]
Wine(const char * l, int y, const int yr[], const int bot[]);
// initialize label to l, number of years to y,
// create array objects of length y
Wine(const char * l, int y);
Wine类应该有一个GetBottles()方法,它根据Wine对象能够存储几种年份(y),提示用户输入年份和瓶数。方法Label()返回一个指向葡萄酒名称的引用。sum()方法返回Pair对象中第二个valarray<int>
对象中的瓶数总和。
测试程序应提示用户输入葡萄酒名称、元素个数以及每个元素存储的年份和瓶数等信息。程序将使用这些数据来构造一个Wine对象,然后显示对象中保存的信息。
下面是一个简单的测试程序:
#include <iostream>
#include "winec.h"
int main ( void )
{
using std::cin;
using std::cout;
using std::endl;
cout << "Enter name of wine: ";
char lab[50];
cin.getline(lab, 50);
cout << "Enter number of years: ";
int yrs;
cin >> yrs;
Wine holding(lab, yrs); // store label, years, give arrays yrs elements
holding.GetBottles(); // solicit input for year, bottle count
holding.Show(); // display object contents
const int YRS = 3;
int y[YRS] = {1993, 1995, 1998};
int b[YRS] = { 48, 60, 72};
// create new object, initialize using data in arrays y and b
Wine more("Gushing Grape Red",YRS, y, b);
more.Show();
cout << "Total bottles for " << more.Label() // use Label() method
<< ": " << more.sum() << endl; // use sum() method
cout << "Bye\n";
return 0;
}
实现:
winec.h
#pragma once
#include <iostream>
#include <string>
#include <valarray>
using namespace std;
template<class T1, class T2> class Pair;//前置声明
typedef valarray<int> ArrayInt;
typedef Pair<ArrayInt, ArrayInt> PairArray;
template<class T1, class T2>//模板类
class Pair
{
private:
T1 a;
T2 b;
public:
Pair(const T1& aval, const T2& bval) : a(aval), b(bval) {}
T1& first();
T2& second();
T1 first() const { return a; }
T2 second() const { return b; }
};
//Wine Class
class Wine
{
private:
string m_label;//葡萄酒名称
//Pair<valarray<int>, valarray<int>> m_info;//实例化Pair
//Pair <ArrayInt, ArrayInt> m_info;
PairArray m_info;//葡萄酒具体哪一年和哪一年的瓶数
int m_year;//几个年份
public:
Wine(const char* l, int y, const int yr[], const int bot[]);
Wine(const char* l, int y);
void GetBottles();
const string& GetLabel() const;
int SumBottles() const;
void ShowInfo();
};
template<class T1, class T2>
T1& Pair<T1, T2>::first()
{
return a;
}
template<class T1, class T2>
T2& Pair<T1, T2>::second()
{
return b;
}
winec.cpp
#include "winec.h"
Wine::Wine(const char* l, int y, const int yr[], const int bot[]) : m_label(l), m_year(y),m_info(ArrayInt(yr,y), ArrayInt(bot,y))
{
}
Wine::Wine(const char* l, int y) : m_label(l), m_year(y), m_info(ArrayInt(0,0), ArrayInt(0,0))
{
}
void Wine::GetBottles()
{
cout << "Enter " << m_label << " data for " << m_year << endl;
ArrayInt a(m_year);
ArrayInt b(m_year);
for (int i = 0; i < m_year; i++)
{
cout << "Enter year: ";
cin >> a[i];
cout << "Enter bottles for thar year: ";
cin >> b[i];
}
m_info.first() = std::move(a);
m_info.second() = move(b);
}
const string& Wine::GetLabel() const
{
return m_label;
}
int Wine::SumBottles() const
{
return m_info.second().sum();
}
void Wine::ShowInfo()
{
cout << "Wine: " << m_label << endl;
cout << " Year Bottles" << endl;
for (int i = 0; i < m_year; i++)
{
cout << " " << m_info.first()[i] << " " << m_info.second()[i] << endl;
}
}
2.采用私有继承而不是包含来完成编程编程练习1。同样,一些typedef可能会有所帮助,另外,您可能还需要考虑诸如下面这样的语句的定义:
PairArray::operator=(PairArray(ArrayInt(),ArrayInt()));
cout << (const string &)(*this);
您设计的类应该可以使用编程练习1中的测试程序进行测试。
实现:
wine.h
#pragma once
#include <iostream>
#include <string>
#include <valarray>
using namespace std;
template<class T1, class T2> class Pair;//前置声明
typedef valarray<int> ArrayInt;
typedef Pair<ArrayInt, ArrayInt> PairArray;
template<class T1, class T2>//模板类
class Pair
{
private:
T1 a;
T2 b;
public:
Pair(const T1& aval, const T2& bval) : a(aval), b(bval) {}
T1& first();
T2& second();
T1 first() const { return a; }
T2 second() const { return b; }
};
//Wine Class
class Wine : private string, private PairArray
{
private:
//string m_label;//葡萄酒名称
//Pair<valarray<int>, valarray<int>> m_info;//实例化Pair
//Pair <ArrayInt, ArrayInt> m_info;
//PairArray m_info;//葡萄酒具体哪一年和哪一年的瓶数
int m_year;//几个年份
public:
Wine(const char* l, int y, const int yr[], const int bot[]);
Wine(const char* l, int y);
void GetBottles();
const string& GetLabel() const;
int SumBottles() const;
void ShowInfo();
};
template<class T1, class T2>
T1& Pair<T1, T2>::first()
{
return a;
}
template<class T1, class T2>
T2& Pair<T1, T2>::second()
{
return b;
}
wine.cpp
#include "winec2.h"
Wine::Wine(const char* l, int y, const int yr[], const int bot[]) : string(l), m_year(y), PairArray(ArrayInt(yr, y), ArrayInt(bot, y))
{
}
Wine::Wine(const char* l, int y) : string(l), m_year(y), PairArray(ArrayInt(0, 0), ArrayInt(0, 0))
{
}
void Wine::GetBottles()
{
cout << "Enter " << (const string&)*this << " data for " << m_year << endl;
ArrayInt a(m_year);
ArrayInt b(m_year);
for (int i = 0; i < m_year; i++)
{
cout << "Enter year: ";
cin >> a[i];
cout << "Enter bottles for thar year: ";
cin >> b[i];
}
PairArray::first() = std::move(a);
PairArray::second() = move(b);
}
const string& Wine::GetLabel() const
{
return (const string&)*this;
}
int Wine::SumBottles() const
{
return PairArray::second().sum();
}
void Wine::ShowInfo()
{
cout << "Wine: " << (const string&)*this << endl;
cout << " Year Bottles" << endl;
for (int i = 0; i < m_year; i++)
{
cout << " " << PairArray::first()[i] << " " << PairArray::second()[i] << endl;
}
}
3.定义一个QueueTp模板。然后在一个类似于程序清单14.12的程序中创建一个指向Worker的指针队列(参见程序清单14.10中的定义),并用该队列来测试它。
实现:
queue.h
#pragma once
#include <iostream>
#include <string>
using namespace std;
//Worker class
class Worker
{
private:
string fullname;
long id;
public:
Worker() :fullname("none one"), id(0){}
Worker(const string& s, long n) :fullname(s), id(n){}
~Worker(){}
void Set();
void Show() const;
};
//Queue Template
template<class T>
class Queue
{
private:
enum{Q_SIZE = 10};
struct Node
{
T item;
struct Node* next;
};
Node* front;
Node* rear;
int items;
const int qsize;
public:
Queue(int qs = Q_SIZE);
~Queue();
bool isempty()const;
bool isfull()const;
int queuecount()const;
bool enqueue(const T& item);
bool dequeue(T& item);
};
template<class T>
Queue<T>::Queue(int qs):qsize(qs)
{
front = rear = nullptr;
items = 0;
}
template<class T>
Queue<T>::~Queue()
{
Node* temp;
if (front != NULL)
{
temp = front;
front = front->next;
delete temp;
}
}
template<class T>
bool Queue<T>::isempty() const
{
return items == 0;
}
template<class T>
inline bool Queue<T>::isfull() const
{
return items == qsize;
}
template<class T>
int Queue<T>::queuecount() const
{
return items;
}
template<class T>
bool Queue<T>::enqueue(const T& item)
{
if (isfull())
{
return false;
}
Node* add = new Node;
add->item = item;
add->next = nullptr;
items++;
if (front == nullptr)
{
front = add;
}
else
{
rear->next = add;
}
rear = add;
return true;
}
template<class T>
bool Queue<T>::dequeue(T& item)
{
if (front == nullptr)
{
return false;
}
item = front->item;
items--;
Node* temp = front;
front = front->next;
delete temp;
if (items == 0)
{
rear = nullptr;
}
return true;
}
queue.cpp
#include "queue.h"
//Worker methods
void Worker::Set()
{
cout << "Enter worker's fullname:";
getline(cin, fullname);
cout << "Enter worker's ID:";
while (cin.get() != '\n');
}
void Worker::Show() const
{
cout << "Name:" << fullname << endl;
cout << "Employee ID:" << id << endl;
}
main.cpp
#include "queue.h"
int main(void)
{
Queue<Worker* > wk(3);//3个Worker*
Worker w1;
w1.Set();
w1.Show();
wk.enqueue(&w1);
cout << "Now queue has:" << wk.queuecount() << " objects." << endl;
Worker w2;
w2.Set();
w2.Show();
wk.enqueue(&w2);
cout << "Now queue has:" << wk.queuecount() << " objects." << endl;
Worker w3;
w3.Set();
w3.Show();
wk.enqueue(&w3);
cout << "Now queue has:" << wk.queuecount() << " objects." << endl;
return 0;
}
4.Person类保存人的名和姓。除构造函数外,它还有Show()方法,用于显示名和姓。Gunsliger类以Person类为虚基类派生而来,它包含一个Dram()成员,该方法返回一个double值,表示枪手的拔枪时间。这个类还包含一个int成员,表示枪手枪上的刻痕数。最后,这个类还包含一个Show()函数,用于显示所有信息。
PokerPlayer类以Person类为虚基类派生而来。它包含一个Draw()成员,该函数返回一个1~52的随机数,用于表示扑克牌的值(也可以定义一个Card类,其中包含花色和面值成员,然后让Draw()返回一个Card对象)。PokerPlayer类使用Person类的show()函数。BadDude()类从Gunsliger和PokerPlayer类公有派生而来。它包含Gdraw()成员(返回坏蛋拔枪的时间)和Cdraw()成员(返回下一张扑克牌),另外还有一个合适的Show()函数。请定义这些类和方法以及其他必要的方法(如用于设置对象值的方法),并使用一个类似于程序清单14.12的简单程序对它们进行测试。
实现:
//无
5.下面是一些类声明:
//emp.h
#pragma once
#include<iostream>
#include<string>
using std::string;
using std::ostream;
using std::cout;
using std::endl;
using std::cin;
//using std::getline;
class abstr_emp
{
private:
string fname;
string lname;
string job;
public:
abstr_emp();
abstr_emp(const string& fn, const string& ln, const string& j);
virtual void ShowAll() const;
virtual void SetAll();
friend ostream& operator<<(ostream& os, const abstr_emp& e);
virtual ~abstr_emp() = 0;//纯虚函数
};
class employee : public abstr_emp
{
public:
employee();
employee(const string& fn, const string& ln, const string& j);
virtual void ShowAll()const;
virtual void SetAll();
};
class manager : virtual public abstr_emp
{
private:
int inchargeof;
protected:
int InChargeOf() const { return inchargeof; }
int& InChargeOf() { return inchargeof; }
public:
manager();
manager(const string& fn, const string& ln, const string& j, int ico = 0);
manager(const abstr_emp& e, int ico);
manager(const manager& m);
virtual void ShowAll()const;
virtual void SetAll();
};
class fink : virtual public abstr_emp
{
private:
string reportsto;
protected:
const string ReportsTo()const { return reportsto; }
string& ReportsTo() { return reportsto; }
public:
fink();
fink(const string& fn, const string& ln, const string& j, const string& rpo);
fink(const abstr_emp& e, const string& rpo);
fink(const fink& e);
virtual void ShowAll()const;
virtual void SetAll();
};
class highfink : public manager, public fink
{
public:
highfink();
highfink(const string& fn, const string& ln, const string& j, const string& rpo, int ico);
highfink(const abstr_emp& e, const string& rpo, int ico);
highfink(const fink& f, int ico);
highfink(const manager& m, const string& rpo);
highfink(const highfink& h);
virtual void ShowAll()const;
virtual void SetAll();
};
注意,该类层次结构使用了带虚基类的MI,所以要牢记在这种情况下用于构造函数初始化列表的特殊规则。还需要注意的是,有些方法被声明为保护的。这可以简化一些highfink方法的代码(例如,如果highfink::ShowAll()只是调用fink::ShowAll()和manager::ShowAll(),则它将调用abstr_emp::ShowAll()两次)。请提供类方法的实现,并在一个程序中对这些类进行测试。下面是一个小型测试程序:
实现:
emp.cpp
#include "emp.h"
//abstr_emp methods
abstr_emp::abstr_emp():fname("none"), lname("none"),job("none")
{
}
abstr_emp::abstr_emp(const string& fn, const string& ln, const string& j) : fname(fn), lname(ln),job(j)
{
}
void abstr_emp::ShowAll() const
{
cout << "fname:" << fname << endl;
cout << "lname:" << lname << endl;
cout << "job:" << job << endl;
}
void abstr_emp::SetAll()
{
cout << "Enter the first name:";
getline(cin, fname);
cout << "Enter the last name:";
getline(cin, lname);
cout << "Enter the job:";
getline(cin, job);
}
abstr_emp::~abstr_emp()
{
}
ostream& operator<<(ostream& os, const abstr_emp& e)
{
os << "fname:" << e.fname << endl;
os << "lname:" << e.lname << endl;
os << "job:" << e.job << endl;
return os;
}
//employee methods
employee::employee() : abstr_emp()
{
}
employee::employee(const string& fn, const string& ln, const string& j) : abstr_emp(fn,ln,j)
{
}
void employee::ShowAll() const
{
abstr_emp::ShowAll();
}
void employee::SetAll()
{
abstr_emp::SetAll();
}
//manager methods
manager::manager() : abstr_emp(),inchargeof(0)
{
}
manager::manager(const string& fn, const string& ln, const string& j, int ico) : abstr_emp(fn, ln, j),inchargeof(ico)
{
}
manager::manager(const abstr_emp& e, int ico) : abstr_emp(e), inchargeof(ico)
{
}
manager::manager(const manager& m) : abstr_emp(m)
{
inchargeof = m.inchargeof;
}
void manager::ShowAll() const
{
abstr_emp::ShowAll();
cout << "ico:" << inchargeof << endl;
}
void manager::SetAll()
{
abstr_emp::SetAll();
cout << "ico:";
cin >> inchargeof;
}
//fink methods
fink::fink() : abstr_emp(), reportsto("none")
{
}
fink::fink(const string& fn, const string& ln, const string& j, const string& rpo): abstr_emp(fn, ln, j), reportsto(rpo)
{
}
fink::fink(const abstr_emp& e, const string& rpo): abstr_emp(e), reportsto(rpo)
{
}
fink::fink(const fink& e):abstr_emp(e)
{
reportsto = e.reportsto;
}
void fink::ShowAll() const
{
abstr_emp::ShowAll();
cout << "reportsto:" << reportsto << endl;
}
void fink::SetAll()
{
abstr_emp::SetAll();
cout << "reportsto:";
cin >> reportsto;
}
//highfink methods
highfink::highfink() : abstr_emp(),manager(),fink()
{
}
highfink::highfink(const string& fn, const string& ln, const string& j, const string& rpo, int ico) : abstr_emp(fn,ln,j), manager(fn,ln, j,ico), fink(fn,ln,j,rpo)
{
}
highfink::highfink(const abstr_emp& e, const string& rpo, int ico) : abstr_emp(e), manager(e,ico), fink(e,rpo)
{
}
highfink::highfink(const fink& f, int ico): abstr_emp(f),manager(f,ico),fink(f)
{
}
highfink::highfink(const manager& m, const string& rpo): abstr_emp(m),manager(m),fink(m,rpo)
{
}
highfink::highfink(const highfink& h): abstr_emp(h), manager(h),fink(h)
{
}
void highfink::ShowAll() const
{
fink::ShowAll();
manager::ShowAll();
}
void highfink::SetAll()
{
fink::SetAll();
manager::SetAll();
}
main.cpp
#include "emp.h"
int main(void)
{
employee em("Trip", "Harris", "Thumper");
cout << em << endl;
em.ShowAll();
manager ma("Amorphia", "Spindragon", "Nuancer", 5);
cout << ma << endl;
ma.ShowAll();
fink fi("Matt", "Oggs", "Oiler", "Juno Barr");
cout << fi << endl;
fi.ShowAll();
highfink hf(ma, "Curly Kew"); // recruitment?
hf.ShowAll();
cout << "Press a key for next phase:\n";
cin.get();
highfink hf2;
hf2.SetAll();
cout << "Using an abstr_emp * pointer:\n";
abstr_emp* tri[4] = { &em, &fi, &hf, &hf2 };
for (int i = 0; i < 4; i++)
tri[i]->ShowAll();
return 0;
}
问题:
1.为什么没有定义赋值运算符?
答:没使用动态内存分配;成员都是string类型,有赋值运算符重载函数。
2.为什么要将ShowAll()和SetAll()定义为虚的?
答:不同的派生类里都有ShowAll()SetAll()实现,实现的方法不一样,为了实际使用的对象去调用正确的ShowAll()SetAll()方法。
3.为什么要将abstr_emp定义为虚基类?
答:虚基类使得从多个类派生出的对象只继承一个基类对象,如果不把abstr_emp定义为虚基类,highfink类对象里会存在2个abstr_emp的副本,既占用内存空间又容易冲突。
4.为什么highfink类没有数据成分?
答:完全继承了manager和fink里的私有成员,足够去描述highfink的成分。
5.为什么只需要一个operator<<()版本?
答:派生出的类都有ShowAll()方法,没必要再去定义operator<<。
如果使用下面的代码替换程序的结尾部分,将会发生什么情况?
abstr_emp tri[4] = {em, fi, hf, hf2};
for (int i = 0; i < 4; i++)
tri[i].ShowAll();
答:编译报错,抽象基类abstr_emp无法创建对象或对象数组。