14.7 编程练习
1.Wine类有一个string类对象成员(参见第4章)和一个Pair对象(参见本章):其中前者用来存储葡萄酒的名称,而后者有2个valarry<int>对象(参见本章),这两个valarry<int>对象分别保存了葡萄酒的酿造年份和该年生产的瓶数。例如,Pair的第1个valarray<int>对象可能为1988、1992和1996,第二个valarry<int> 对象可能为24、48和144瓶。Wine最好有1个int成员用于存储年数。另外,一些typedef可能有助于简化编程工作:
typedef std::valarry<int> ArrayInt;
typedef Pair<ArrayInt, ArrayInt> PairArray;
这样,PairArray表示的类型是Pair<std::valarry<int>,std::valarray<int>>。使用包含来实现Wine类,并用一个简单的程序对其进行测试。Wine类应该有一个默认构造函数以及如下的构造函数:
Wine(const char* l, int y, const int yr[], ocnst int bot[]);
Wine(const char* l, int y);
Wine类应该有一个GetBottles()方法,它根据Wine对象能够存储集中年份(y),提示用户输入年份和瓶数。方法Label()返回一个指向葡萄酒名称的引用。sum()方法返回Pair对象中第二个valarray<int>对象中的瓶数总和。
测试程序应提示用户输入葡萄酒名称、元素个数以及每个元素存储的年份和瓶数等信息。程序将使用这些数据来构造一个Wine对象,然后显示对象中保存的信息。
下面是一个简单的测试程序:
int main()
{
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);
holding.GetBottles();
holding.Show();
const int YRS = 3;
int y[YRS] = {1993, 1995, 1998};
int b[YRS] = {48, 60, 72};
Wine more("Gushing Grape Red", YRS, y, b);
more.Show();
cout << "Total bottle for " << more.Label()
<<": " << more.sum() << endl;
cout << "Bye\n";
return 0;
}
下面是该程序的运行情况:
Enter name of wine: Gully Wash
Enter number of years: 4
Enter Gully Wash data for 4 year(s):
Enter year: 1988
Enter bottles for that year: 42
Enter year: 1994
Enter bottles for that year: 58
Enter year: 1998
Enter bottles for that year: 122
Enter year: 2001
Enter bottles for that year: 144
Wine: Gully Wash
Year Bottles
1988 42
1994 58
1998 122
2001 144
Wine: Gushing Grape Red
Year Bottles
1993 48
1995 60
1998 72
Total bottle for Gushing Grape Red: 180
Bye
代码:
Test.h
//
// Test.h
// HelloWorld
//
// Created by feiyin001 on 16/12/21.
// Copyright (c) 2016年 FableGame. All rights reserved.
//
#ifndef _Test_H_
#define _Test_H_
#include <iostream>
#include <string>
#include <valarray>
using namespace std;
namespace FableGame
{
typedef std::valarray<int> ArrayInt;
typedef pair<ArrayInt, ArrayInt> PairArray;
class Wine
{
private:
string name;
PairArray info;
int yearNum;
public:
Wine(const char* l, int y, const int yr[], const int bot[]);
Wine(const char* l, int y);
void GetBottles();
void Show();
string& Label(){ return name;}
int sum();
};
}
#endif
Test.cpp
//
// Test.cpp
// HelloWorld
//
// Created by feiyin001 on 16/12/21.
// Copyright (c) 2016年 FableGame. All rights reserved.
//
#include "Test.h"
#include <iostream>
#include <cstdlib>
using namespace std;
using namespace FableGame;
Wine::Wine(const char* l, int y, const int yr[], const int bot[])
{
name = l;
yearNum = y;
ArrayInt years(y);
ArrayInt bots(y);
for (int i = 0; i < y; ++i)
{
years[i] = yr[i];
bots[i] = bot[i];
}
info = make_pair(years, bots);
}
Wine::Wine(const char* l, int y)
{
name = l;
yearNum = y;
}
void Wine::GetBottles()
{
cout << "Enter " << name << " data for " << yearNum << " year(s):\n";
info.first.resize(yearNum);
info.second.resize(yearNum);
for (int i = 0; i < yearNum; ++i)
{
cout << "Enter year: ";
cin >> info.first[i];
cout << "Enter bottles for that year: ";
cin >> info.second[i];
}
}
void Wine::Show()
{
cout << "Wine: " << name << endl;
cout << "\t\tYear\tBottles\n";
for (int i = 0; i < yearNum; ++i)
{
cout << "\t\t" << info.first[i] << "\t" << info.second[i]<<endl;
}
}
int Wine::sum()
{
return info.second.sum();
}
这道题考验的是对于一些模板类的使用。以及has-a关系的应用。
2.采用私有继承而不是包含来完成编程练习1.同样,一些typedef可能会有所帮助,另外,您可能还需要考虑诸如下面这样的语句的含义:
PairArray::operator=(PairArray(ArrayInt(), ArrayInt()));
cout << (const string&)(*this);
您设计的类应该可以使用编程练习1中的测试程序进行测试。
//
// Test.h
// HelloWorld
//
// Created by feiyin001 on 16/12/21.
// Copyright (c) 2016年 FableGame. All rights reserved.
//
#ifndef _Test_H_
#define _Test_H_
#include <iostream>
#include <string>
#include <valarray>
using namespace std;
namespace FableGame
{
typedef std::valarray<int> ArrayInt;
typedef pair<ArrayInt, ArrayInt> PairArray;
class Wine: private string, private PairArray
{
private:
int yearNum;
public:
Wine(const char* l, int y, const int yr[], const int bot[]);
Wine(const char* l, int y);
void GetBottles();
void Show();
string& Label();
int sum();
};
}
#endif
Test.cpp
//
// Test.cpp
// HelloWorld
//
// Created by feiyin001 on 16/12/21.
// Copyright (c) 2016年 FableGame. All rights reserved.
//
#include "Test.h"
#include <iostream>
#include <cstdlib>
using namespace std;
using namespace FableGame;
Wine::Wine(const char* l, int y, const int yr[], const int bot[])
{
(string&)(*this) = l;
yearNum = y;
ArrayInt years(y);
ArrayInt bots(y);
for (int i = 0; i < y; ++i)
{
years[i] = yr[i];
bots[i] = bot[i];
}
(PairArray&)(*this) = make_pair(years, bots);
}
Wine::Wine(const char* l, int y)
{
(string)(*this) = l;
yearNum = y;
}
void Wine::GetBottles()
{
cout << "Enter " << (const string&)(*this) << " data for " << yearNum << " year(s):\n";
PairArray& info = (PairArray&)(*this);
info.first.resize(yearNum);
info.second.resize(yearNum);
for (int i = 0; i < yearNum; ++i)
{
cout << "Enter year: ";
cin >> info.first[i];
cout << "Enter bottles for that year: ";
cin >> info.second[i];
}
}
string& Wine::Label()
{
return (string&)(*this);
}
void Wine::Show()
{
cout << "Wine: " << (const string&)(*this) << endl;
cout << "\t\tYear\tBottles\n";
PairArray& info = (PairArray&)(*this);
for (int i = 0; i < yearNum; ++i)
{
cout << "\t\t" << info.first[i] << "\t" << info.second[i]<<endl;
}
}
int Wine::sum()
{
return ((PairArray&)(*this)).second.sum();
}
利用引用可以大幅减少修改的程度。
4.Person类保存人的名和姓。除构造函数外,它还有Show()方法,用于显示名和姓。Gunslmger类以Person类为虚基类派生而来,它包含一个Draw()成员,该方法返回一个double值,表示枪手的拔枪时间。这个类还包含一个int成员,表示枪手枪上的刻痕数。最后,这个类还包含一个Show()函数,用于显示所有这些信息。
PokerPlayer类以Person类为虚基类派生而来。它包含一个Draw()成员,该函数返回一个1~52的随机数,用于扑克牌的值(也可以用定义一个Card类,其中包含花色和面值成员,然后让Draw()返回一个Card对象)。PokerPlayer类使用Person类的show()函数。BadDude()类从Gunslinger何PokerPlayer类公有派生而来。它包含Gdraw()成员(返回坏蛋拔枪时间)和Cdraw()成员(返回下一张扑克牌),另外还有一个合适的show()函数。请定义这些类和方法以及其他必要的方法(如用于设置对象值的方法),并使用一些类似程序清单14.12的简单程序对它们进行测试。
Test.h
//
// Test.h
// HelloWorld
//
// Created by feiyin001 on 16/12/21.
// Copyright (c) 2016年 FableGame. All rights reserved.
//
#ifndef _Test_H_
#define _Test_H_
#include <iostream>
#include <string>
#include <valarray>
using namespace std;
namespace FableGame
{
class Person
{
private:
string surname;//姓
string personalName;//名
public:
Person(){}
Person(const string& s, const string& p)
{
surname = s;
personalName = p;
}
virtual ~Person(){}
virtual void set()
{
cout << "Enter surname: ";
getline(cin, surname);
cout << "Enter personal name: ";
getline(cin, personalName);
}
virtual void show()
{
cout << personalName << " " << surname;
}
};
class Gunslinger :public virtual Person
{
private:
double gunTime;
int gunNum;
void setDate()
{
cout << "Enter gunTime: ";
cin >> gunTime;
cout << "Enter gunNum: ";
cin >> gunNum;
cin.get();
}
public:
virtual ~Gunslinger(){}
double draw(){ return gunTime;}
virtual void show()
{
Person::show();
cout << " " << gunTime << " " << gunNum;
}
virtual void set()
{
Person::set();
setDate();
}
};
class PokerPlayer: public virtual Person
{
public:
int draw()
{
srand(time(0));
return rand()%52 + 1;
}
virtual ~PokerPlayer(){}
};
class BadDude: public Gunslinger, public PokerPlayer
{
public:
double gdraw()
{
return Gunslinger::draw();
}
int cdraw()
{
return PokerPlayer::draw();
}
void show()
{
Gunslinger::show();
}
};
}
#endif
main.cpp
//
// main.cpp
// HelloWorld
//
// Created by feiyin001 on 16/12/21.
// Copyright (c) 2016年 FableGame. All rights reserved.
//
#include <iostream>
#include "Test.h"
using namespace std;
using namespace FableGame;
const int ARRSIZE = 5;
int main()
{
Person* person[ARRSIZE];
int ct = 0;
for (ct = 0; ct < ARRSIZE; ct++) {
int choice;
cout << "Enter the person category:\n"
<< "1: person 2:gunslinger"
<< "3: pokerplayer 4: BadDude 0:quit\n";
cin >> choice;
while (choice < 0 || choice > 4) {
cout << "Please enter 1, 2, 3, 4 or 0: ";
cin >> choice;
}
if (choice == 0) {
break;
}
cin.get();
switch (choice) {
case 1:
person[ct] = new Person;
break;
case 2:
person[ct] = new Gunslinger;
break;
case 3:
person[ct] = new PokerPlayer;
break;
case 4:
person[ct] = new BadDude;
break;
}
person[ct]->set();
}
cout << "\nAll PersonL\n";
for (int i = 0; i < ct; ++i) {
cout << endl;
person[i]->show();
}
for (int i = 0; i < ct; ++i) {
delete person[i];
}
cout << "Bye.\n";
return 0;
}
漏得最多的地方就是各个函数的virtual。导致读写和析构都出问题。
这道题主要检验多重继承的把握。关键是准确调用父类的函数,还有注意构造函数和析构函数。
5.下面是一些类声明:
class AbstrEmp
{
private:
string _fname;
string _lname;
string _job;
public:
AbstrEmp();
AbstrEmp(const std::string& fn, const string& ln, const string& j);
virtual void showAll()const;
virtual void setAll();
friend ostream& operator<<(ostream& os, const AbstrEmp& e);
virtual ~AbstrEmp() = 0;
};
class Employee : public AbstrEmp
{
public:
Employee();
Employee(const string& fn, const string& ln, const string& j);
virtual void showAll()const;
virtual void setAll();
};
class Manager : virtual public AbstrEmp
{
private:
int _inchargeof;
protected:
int inChargeOf()const
{
return _inchargeof;
}
int& inChargeOf()
{
return _inchargeof;
}
public:
Manager();
Manager(const string& fn, const string& ln, const string& j0, int ico = 0);
Manager(const AbstrEmp& e, int ico);
Manager(const Manager& m);
virtual void showAll()const;
virtual void setAll();
};
class Fink : virtual public AbstrEmp
{
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 AbstrEmp& 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 AbstrEmp& 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方法的代码(例如,如果HightFink::showAll()只是调用Fink::showAll()和Manager::showAll(),则它将调用abstr_emp::ShowAll()两次)。提供类方法的实现,并在一个程序中对这些类进行测试。下面是一个小型测试程序:
#include <iostream>
#include "Test.h"
#include <string>
using namespace std;
using namespace FableGame;
int main(int argc, const char * argv[])
{
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");
hf.showAll();
cout << "Press a key for next phase:\n";
cin.get();
HighFink hf2;
hf2.setAll();
cout << "Using an abstr_emp * pointer:\n";
AbstrEmp* tri[4] = { &em, &fi, &hf, &hf2 };
for (int i = 0; i < 4; ++i)
{
tri[i]->showAll();
}
return 0;
}
为什么没有定义赋值运算符?
为什么要将ShowAll()和SetAll()定义为虚的?
为什么要将abstr_emp定义为虚基类?
为什么highfink类没有数据部分?
为什么只需一个operator<<()版本?
如果使用下面的代码替换程序的结尾部分,将会发生什么情况?
abstr_emp str[4] = {em, fi, hf, hf2};
for(int i = 0; i < 4; ++i)
tr[i].showAll()
程序:
Test.h
//
// Test.h
// HelloWorld
//
// Created by feiyin001 on 16/12/21.
// Copyright (c) 2016年 FableGame. All rights reserved.
//
#ifndef _Test_H_
#define _Test_H_
#include <iostream>
#include <string>
using namespace std;
namespace FableGame
{
class AbstrEmp
{
private:
string _fname;
string _lname;
string _job;
protected:
void setData();
void showData()const;
public:
AbstrEmp();
AbstrEmp(const std::string& fn, const string& ln, const string& j);
virtual void showAll()const;
virtual void setAll();
friend ostream& operator<<(ostream& os, const AbstrEmp& e);
virtual ~AbstrEmp();
};
class Employee : public AbstrEmp
{
public:
Employee();
Employee(const string& fn, const string& ln, const string& j);
virtual void showAll()const;
virtual void setAll();
};
class Manager : virtual public AbstrEmp
{
private:
int _inchargeof;
protected:
int inChargeOf()const
{
return _inchargeof;
}
int& inChargeOf()
{
return _inchargeof;
}
void setData();
void showData()const;
public:
Manager();
Manager(const string& fn, const string& ln, const string& j0, int ico = 0);
Manager(const AbstrEmp& e, int ico);
Manager(const Manager& m);
virtual void showAll()const;
virtual void setAll();
};
class Fink : virtual public AbstrEmp
{
private:
string _reportsto;
protected:
const string reportsTo() const{ return _reportsto; }
string& reportsTo(){ return _reportsto; }
void setData();
void showData()const;
public:
Fink();
Fink(const string& fn, const string& ln, const string& j, const string& rpo);
Fink(const AbstrEmp& 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 AbstrEmp& 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();
};
}
#endif
Test.cpp
//
// Test.cpp
// HelloWorld
//
// Created by feiyin001 on 16/12/21.
// Copyright (c) 2016年 FableGame. All rights reserved.
//
#include "Test.h"
#include <iostream>
using namespace std;
using namespace FableGame;
FableGame::AbstrEmp::AbstrEmp()
{
_fname = "";
_lname = "";
_job = "";
}
FableGame::AbstrEmp::AbstrEmp(const std::string& fn, const string& ln, const string& j)
{
_fname = fn;
_lname = ln;
_job = j;
}
void FableGame::AbstrEmp::showAll() const
{
showData();
}
void FableGame::AbstrEmp::setAll()
{
setData();
}
FableGame::AbstrEmp::~AbstrEmp()
{
}
void FableGame::AbstrEmp::showData()const
{
cout << "FName:" << _fname << " LName:" << _lname << " Job:" << _job << endl;
}
void FableGame::AbstrEmp::setData()
{
cout << "Enter FName:";
getline(cin, _fname);
cout << "Enter LName:";
getline(cin, _lname);
cout << "Enter Job:";
getline(cin, _job);
}
ostream& FableGame::operator<<(ostream& os, const AbstrEmp& e)
{
os << e._fname << " " << e._lname << " " << e._job ;
return os;
}
FableGame::Employee::Employee()
{
}
FableGame::Employee::Employee(const string& fn, const string& ln, const string& j) : AbstrEmp(fn, ln, j)
{
}
void FableGame::Employee::showAll() const
{
AbstrEmp::showData();
}
void FableGame::Employee::setAll()
{
AbstrEmp::setData();
}
FableGame::Manager::Manager()
{
_inchargeof = 0;
}
FableGame::Manager::Manager(const string& fn, const string& ln, const string& j0, int ico /*= 0*/) : AbstrEmp(fn, ln, j0), _inchargeof(ico)
{
}
FableGame::Manager::Manager(const AbstrEmp& e, int ico) : AbstrEmp(e), _inchargeof(ico)
{
}
FableGame::Manager::Manager(const Manager& m) : AbstrEmp(m)
{
_inchargeof = m._inchargeof;
}
void FableGame::Manager::showAll() const
{
AbstrEmp::showData();
showData();
}
void FableGame::Manager::setAll()
{
AbstrEmp::setData();
setData();
}
void FableGame::Manager::setData()
{
cout << "Enter inchargeof:";
cin >> _inchargeof;
cin.get();
}
void FableGame::Manager::showData() const
{
cout << "inchargeof:" << _inchargeof << endl;
}
FableGame::Fink::Fink()
{
}
FableGame::Fink::Fink(const string& fn, const string& ln, const string& j, const string& rpo) : AbstrEmp(fn, ln, j), _reportsto(rpo)
{
}
FableGame::Fink::Fink(const AbstrEmp& e, const string& rpo) : AbstrEmp(e), _reportsto(rpo)
{
}
FableGame::Fink::Fink(const Fink& e) : AbstrEmp(e)
{
_reportsto = e._reportsto;
}
void FableGame::Fink::showAll() const
{
AbstrEmp::showData();
showData();
}
void FableGame::Fink::setAll()
{
AbstrEmp::setData();
setData();
}
void FableGame::Fink::setData()
{
cout << "Enter _reportsto:";
cin >> _reportsto;
cin.get();
}
void FableGame::Fink::showData() const
{
cout << "reportsto:" << _reportsto << endl;
}
FableGame::HighFink::HighFink()
{
}
FableGame::HighFink::HighFink(const string& fn, const string& ln, const string& j, const string& rpo, int ico) :
AbstrEmp(fn, ln, j), Manager(fn, ln, j, ico), Fink(fn, ln, j, rpo)
{
}
FableGame::HighFink::HighFink(const AbstrEmp& e, const string& rpo, int ico) :AbstrEmp(e), Manager(e, ico), Fink(e, rpo)
{
}
FableGame::HighFink::HighFink(const Fink& f, int ico) : Fink(f), Manager(f, ico), AbstrEmp(f)
{
}
FableGame::HighFink::HighFink(const Manager& m, const string& rpo) : Fink(m, rpo), Manager(m), AbstrEmp(m)
{
}
FableGame::HighFink::HighFink(const HighFink& h) : Fink(h), Manager(h), AbstrEmp(h)
{
}
void FableGame::HighFink::showAll() const
{
AbstrEmp::showData();
Fink::showData();
Manager::showData();
}
void FableGame::HighFink::setAll()
{
AbstrEmp::setData();
Fink::setData();
Manager::setData();
}
main.cpp
#include <iostream>
#include "Test.h"
#include <string>
using namespace std;
using namespace FableGame;
int main(int argc, const char * argv[])
{
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");
hf.showAll();
cout << "Press a key for next phase:\n";
HighFink hf2;
hf2.setAll();
cout << "Using an abstr_emp * pointer:\n";
AbstrEmp* tri[4] = { &em, &fi, &hf, &hf2 };
for (int i = 0; i < 4; ++i)
{
tri[i]->showAll();
}
return 0;
}
为什么没有定义赋值运算符?
所有类的数据成员,都没有包含指针,在构造函数里面也没有使用new。所以不需要。
为什么要将ShowAll和SetAll定义为虚?
这样就可以在使用基类指针的时候,可以正确调用对象的函数。
为什么将abstr_emp定义为虚基类?
这样就不会有多个abstr_emp子对象了。
为什么highfink类没有数据部分?
这个看类的设计,没有新的数据需要记录就没有啦。
为什么只需要一个operator<<()版本?
这个。。。看需求吧。一个基类的版本肯定不能满足派生类的所有信息输出的。
使用新的代码,则数组内的元素是AbstrEmp。
写在最后:
想不到题目没有模板类的实现。不过也对,这本书也算是基础的内容,模板类对很多人来说,都是使用的。
更多涉及的是算法的实现吧。对于业务层的功能,好像很少需要自己创建模板类。至少我没写过。