一、复习题
1. 当以 A 栏的类为基类时,B 栏的类更适合采用公有派生还是私有派生?
答:公有派生是 IS-A 关系,派生类相当于一种特殊的基类。而私有派生是 HAS-A 关系,相当于派生类中包含了基类。
a. 熊和北极熊,很明显北极熊是一种特殊的熊——公有派生
b. 厨房和房子,显然房子包含厨房——私有派生
c. 人和程序员,程序员首先是人,只不过学会了IT技术——公有派生
d. 马和驯马师,驯马师要驯马,所以驯马师应该包含马这个类——私有继承
e. 人、机车和司机,首先司机是一个人,所以人和司机是公有派生,其次司机需要机车用来驾驶,所以司机类应该包含机车类,司机和机车属于私有派生
2. 假设有下面的定义。
// Frabjous 类声明
class Frabjous
{
private:
char fab[20];
public:
Frabjous(const char* s = "C++") : fab(s) { }
virtual void tell() { cout << fab; }
};
// Gloam 类声明
class Gloam
{
private:
int glip;
Frabjous fb;
public:
Gloam(int g = 0, const char* s = "C++");
Gloam(int g, const Frabjous& f);
void tell();
};
同时假设 Gloam 版本的tell()应显示glip和fb的值,请为这 3 个 Gloam 方法提供定义。
答:代码如下:
// Gloam 类方法定义
Gloam::Gloam(int g = 0, const char* s = "C++")
: glip(g)
, fb(s)
{
}
Gloam::Gloam(int g, const Frabjous& f)
: glip(g)
, fb(f)
{
}
void Gloam::tell()
{
fb.tell(); // 通过包含的类对象调用该类方法访问该类私有成员
cout << ", " << glip << endl;
}
3. 假设有下面的定义。
// Frabjous 基类声明
class Frabjous
{
private:
char fab[20];
public:
Frabjous(const char* s = "C++") : fab(s) { }
virtual void tell() { cout << fab; }
};
// Gloam 私有派生类声明
class Gloam : private Frabjous
{
private:
int glip;
public:
Gloam(int g = 0; const char* s = "C++");
Gloam(int g, const Frabjous& f);
void tell();
};
同时假设 Gloam 版本的tell()应显示 glip 和 fab 的值,请为这 3 个 Gloam 方法提供定义。
答:代码如下:
// Gloam 类方法定义
Gloam::Gloam(int g = 0; const char* s = "C++")
: Frabjous(s)
, glip(g)
{
}
Gloam::Gloam(int g, const Frabjous& f)
: Frabjous(f)
, glip(g)
{
}
void Gloam::tell()
{
// 通过类名限定符调用基类方法
Frabjous::tell();
cout << ", " << glip << endl;
}
4. 假设有下面的定义,它基于程序清单 14.13 中的 Stack 模版和程序清单 14.10 中的Worker 类。
Stack<Worker*> sw;
请写出将生成的类声明。只实现类声明不实现非内联的方法。
答:代码如下:
// Stack 类模版显式实例化
class Stack<Worker*>
{
private:
enum { MAX = 10 };
Worker* items[MAX];
int top;
public:
Stack();
bool isempty() const;
bool isfull() const;
bool push(const Worker*& item);
bool pop(Worker*& item);
};
5. 使用本章中的模版对下面的内容进行定义。
a. string 对象数组;
b. double 数组栈;
c. 指向 Worker 对象的指针的栈数组。
程序清单 14.18 生成了多少个模板类定义?
答:
a. ArrayTP<string, 40> str_arr; 可以根据需要调整数组大小
b. Stack<ArrayTP<double, 40> > stack_arr_d;
c. ArrayTP<Stack<Worker*>, 40> arr_stk_wpr;
程序清单 14.18 生成了 4 个模板类定义
1)ArrayTP<int, 10>sums——int数据类型,长度为10的数组。
2)ArrayTP<double, 10>saves——double数据类型,长度为10的数组。
3)ArrayTP<int, 5>——int数据类型,长度为5的数组。
4)ArrayTP<ArrayTP<int, 5>, 10>twodee——以包含5个int类型的数组为元素,长度为10的数组。本质上是一个10*5的二维数组。
6. 指出虚基类和非虚基类之间的区别。
答:虚基类和非虚基类的差别体现在当派生类的多个基类有共同的祖先时,非虚基类的继承关系中,多个基类的共同祖先会存在多个相同的副本,而虚基类中共同的祖先只会保存一个副本。此外,当使用非虚基类时,派生类从不同的基类那里继承了多个同名的数据成员,如果没有用类名进行限定,将导致二义性。如果使用的是虚基类,派生类中的名称优先于直接或间接祖先类中的相同名称,这样即使不使用限定符也不会导致二义性。
二、编程练习
1. Wine 类有一个 string 类的对象成员(参见第 4 章)和一个 Pair 对象(参见本章)。其中,前者用于存储葡萄酒的名称,而后者有两个 valarray<int>对象(参见本章),这两个 valarray<int>对象分别保存了葡萄酒的酿造年份和该年份生产的瓶数。例如,Pair 的第 1 个 valarray<int>对象可能表示 1988 年、1992 年和 1996 年,第 2 个 valarray<int>对象可能表示 24 瓶、48 瓶和 144 瓶。Wine 最好有一个 int 成员,用于存储年数。另外,一些 typedef 可能有助于简化编程工作。
typedef std :: valarray ArrayInt;
typedef Pair<ArrayInt, ArrayInt> PairArray;
这样,PairArray 表示的是类型 Pair<std::valarray<int>,std::valarray<int> >。使用包含关系来实现 Wine 类,并用一个简单的程序对其进行测试。Wine 类应该有一个默认构造函数以及如下构造函数。
// 初始化标签为 l,初始化年数为 y,
// 初始化 vintage 的年数为 yr[],初始化瓶数为 bot[]
Wine(const char* l, int y, const int yr[], const int bot[]);
// 初始化标签为 l,初始化年数为 y
// 创建关于长度的数组对象
Wine(const char* l, int y);
Wine 类应该有一个 GetBottles()方法,它根据 Wine 对象能够存储几个年份,提示用户输入年份和瓶数。方法Label()返回一个指向葡萄酒名称的引用。sum()方法返回 Pair 对象的第 2 个valarray/对象中的瓶数总和
测试程序应提示用户输入葡萄酒的名称、元素个数以及每个元素存储的年份和瓶数等信息。程序将使用这些数据来构造一个 Wine 对象,然后显示对象中保存的信息。
下面是一个简单的测试程序。
// 头文件
#include "winec.h"
int main()
{
// using 声明
using std::cin;
using std::cout;
using std::endl;
// 输入信息
cout << "Enter the 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 bottles for " << more.Label()
<< ": " << more.sum() << endl;
// 结束
cout << "Bye\n";
return 0;
}
答:两个文件。
winec.h 头文件
#pragma once
// 头文件
#include <iostream>
#include <valarray>
#include <string>
// using 声明
using std::string;
using std::valarray;
// Pair 类模版声明
template <class T1, class T2>
class Pair
{
private:
T1 a;
T2 b;
public:
// 构造函数
Pair(const T1& aval, const T2& bval) : a(aval), b(bval) { }
Pair() { }
// 其他函数
T1& first();
T2& second();
T1 first() const { return a; }
T2 second() const { return b; }
};
// Pair 类模版函数定义
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 类声明
class Wine
{
// typedef 类型声明
typedef valarray<int> ArrayInt;
typedef Pair<ArrayInt, ArrayInt> PairArray;
private:
string name; // 葡萄酒名
int y; // 年数
PairArray info; // 年份和瓶数
public:
// 构造函数
Wine() : name(""), info(), y(0) { }
Wine(const char* l, int y, const int yr[], const int bot[])
: name(l), y(y), info(ArrayInt(yr, y), ArrayInt(bot, y)) { }
Wine(const char* l, int y)
: name(l), y(y), info(ArrayInt(y), ArrayInt(y)) { }
// 其他函数
void GetBottles(); // 设置年份和瓶数
void Show() const; // 显示信息
const string& Label() const; // 返回葡萄酒名
int sum() const; // 返回总瓶数
};
winec.cpp 方法定义文件
// 头文件
#include "winec.h"
// using 声明
using std::cout;
using std::endl;
using std::cin;
// Wine 类方法定义
// 其他函数
void Wine::GetBottles() // 设置年份和瓶数
{
// 取出两个数组
valarray<int>& Y = info.first();
valarray<int>& b = info.second();
cout << "Enter " << name << " data for "
<< y << " year(s):\n";
for (int i = 0; i < y; ++i)
{
// 输入年份和瓶数
cout << "Enter year: ";
int year;
cin >> year;
cout << "Enter bottles for that year: ";
int bottles;
cin >> bottles;
// 插入数据
Y[i] = year;
b[i] = bottles;
}
}
void Wine::Show() const // 显示信息
{
// 取出两个数组
const valarray<int>& Y = info.first();
const valarray<int>& b = info.second();
cout << "Wine: " << name << endl
<< " Year Bottles\n";
for (int i = 0; i < y; ++i)
{
cout << " " << Y[i] << " " << b[i] << endl;
}
}
const string& Wine::Label() const // 返回葡萄酒名
{
return name;
}
int Wine::sum() const // 返回总瓶数
{
// 取出第二个数组
const valarray<int> b = info.second();
return b.sum();
}
运行结果:
2. 采用私有继承而不是包含来完成编程练习1。同样,一些 typedef 可能会有所帮助,另外,你可能还需要考虑诸如下面这样的语句的含义。
PairArray::operator=(PariArray(ArrayInt(), ArrayInt() ) );
cout << (const string& ) (*this);
你设计的类应该可以使用编程练习 1 中的测试程序进行测试。
答:包含关系可以在一个类中创建另一个类的对象,通过该对象来调用该类的方法,且可以创建多个对象。而私有继承,继承了一个该类的对象,调用该类的函数时,需要使用该类名,且一般只能有该类的一个对象。需要修改编程练习 1 的头文件和方法定义文件,然后用测试程序进行测试,结果应该和原来一致。
wince2.h 头文件
#pragma once
// 头文件
#include <iostream>
#include <valarray>
#include <string>
// using 声明
using std::string;
using std::valarray;
// Pair 类模版声明
template <class T1, class T2>
class Pair
{
private:
T1 a;
T2 b;
public:
// 构造函数
Pair(const T1& aval, const T2& bval) : a(aval), b(bval) { }
Pair() { }
// 其他函数
T1& first();
T2& second();
T1 first() const { return a; }
T2 second() const { return b; }
};
// Pair 类模版函数定义
template<class T1, class T2>
T1& Pair<T1, T2>::first()
{
return a;
}
template<class T1, class T2>
T2& Pair<T1, T2>::second()
{
return b;
}
// typedef 类型声明
typedef valarray<int> ArrayInt;
typedef Pair<ArrayInt, ArrayInt> PairArray;
// Wine 类声明
class Wine : private string, private PairArray
{
private:
int y; // 年数
public:
// 构造函数
Wine() : string(), PairArray(), y(0) { }
Wine(const char* l, int y, const int yr[], const int bot[])
: string(l), y(y), PairArray(ArrayInt(yr, y), ArrayInt(bot, y)) { }
Wine(const char* l, int y)
: string(l), y(y), PairArray(ArrayInt(y), ArrayInt(y)) { }
// 其他函数
void GetBottles(); // 设置年份和瓶数
void Show() const; // 显示信息
const string& Label() const; // 返回葡萄酒名
int sum() const; // 返回总瓶数
};
winec2.cpp 方法定义文件
// 头文件
#include "winec2.h"
// using 声明
using std::cout;
using std::endl;
using std::cin;
// Wine 类方法定义
// 其他函数
void Wine::GetBottles() // 设置年份和瓶数
{
cout << "Enter " << (const string&)*this << " data for "
<< y << " year(s):\n";
for (int i = 0; i < y; ++i)
{
// 输入年份和瓶数
cout << "Enter year: ";
int year;
cin >> year;
cout << "Enter bottles for that year: ";
int bottles;
cin >> bottles;
// 插入数据
first()[i] = year; // 调用基类函数
second()[i] = bottles; // 直接调用基类函数
/*((PairArray&)(*this)).first()[i] = year;
((PairArray&)(*this)).second()[i] = bottles;*/
}
}
void Wine::Show() const // 显示信息
{
cout << "Wine: " << (const string&)*this << endl
<< " Year Bottles\n";
for (int i = 0; i < y; ++i)
{
cout << " " << first()[i] << " " <<
second()[i] << endl;
}
}
const string& Wine::Label() const // 返回葡萄酒名
{
return (const string&)*this;
}
int Wine::sum() const // 返回总瓶数
{
return second().sum();
}
运行结果和第一题一样
3. 定义一个 QueueTp 模版。然后再一个类似于程序清单 14.12 的程序中创建一个指向 Worker 的指针队列(参见程序清单 14.10 中的定义),并使用该队列来测试它。
答:通常将模板类的声明和方法的定义都直接放在头文件中。这样做的原因是模板需要在实例化时进行具体的类型推导和代码生成,将定义放在头文件中可以确保在使用模板的地方能够找到完整的定义信息。本题代码是作者照着答案抄的,作者自己写的编译不通过,搞了半天不知道问题在哪里。
QueueTp.h 头文件
#pragma once
// 头文件
#include <iostream>
#include <string>
// using 编译指令
using std::string;
// Worker 类声明
class Worker
{
private:
string fullname;
long _id;
public:
// 构造函数
Worker() : fullname("no one"), _id(0L) { }
Worker(const string& name, long id) : fullname(name), _id(id) { }
// 析构函数
~Worker() { }
// 其他函数
void Set();
void Show() const;
};
// QueueTp 模板类声明
template <class T>
class QueueTp
{
private:
enum { Q_SIZE = 10 };
struct Node {
T item;
Node* next;
};
Node* front; // 头指针
Node* rear; // 尾指针
int items; // 队列长度
const int qsize; // 队列容量
QueueTp(const QueueTp& q) : qsize(0) { }
QueueTp& operator=(const QueueTp& q) { return *this; }
public:
// 构造函数
QueueTp(int qs = Q_SIZE);
// 析构函数
~QueueTp();
// 其他函数
bool isempty() const;
bool isfull() const;
int queuecount() const;
bool enqueue(const T& item);
bool dequeue(T& item);
};
// QueueTp 模板类方法定义
template <class T>
QueueTp<T>::QueueTp(int qs) : qsize(qs)
{
front = rear = nullptr;
items = 0;
}
template <class T>
QueueTp<T>::~QueueTp()
{
Node* temp;
while (front != nullptr)
{
temp = front;
front = front->next;
delete temp;
}
}
template <class T>
bool QueueTp<T>::isempty() const
{
return items == 0;
}
template <class T>
bool QueueTp<T>::isfull() const
{
return items == qsize;
}
template <class T>
int QueueTp<T>::queuecount() const
{
return items;
}
template <class T>
bool QueueTp<T>::enqueue(const T& item)
{
if (isfull()) // 检查是队列是否已满
return false;
Node* temp = new Node; // 申请新的结点
temp->item = item; // 存储信息
temp->next = nullptr;
++items; // 队列长度加1
if (front == nullptr) // 是否头插
front = temp;
else
rear->next = temp;
rear = temp;
return true;
}
template <class T>
bool QueueTp<T>::dequeue(T& item)
{
if (isempty()) // 检查队列是否为空
return false;
item = front->item; // 存储被删结点信息
--items; // 队列长度减1
Node* temp = front;
front = front->next;
delete temp;
if (front == nullptr) // 如果为头删
rear = nullptr;
return true;
}
Test3.cpp 测试文件
// 头文件
#include "QueueTp.h"
// using 声明
using std::cout;
int main()
{
QueueTp<Worker> lolas;
Worker w1;
w1.Set();
lolas.enqueue(w1);
Worker w2;
lolas.dequeue(w2);
w2.Show();
cout << "Bye.\n";
return 0;
}
QueueTp.cpp 方法定义文件
// 头文件
#include "QueueTp.h"
// using 声明
using std::cout;
using std::endl;
using std::cin;
// Worker 类方法定义
void Worker::Show() const
{
cout << "Name: " << fullname << endl;
cout << "Employee ID: " << _id << endl;
}
void Worker::Set()
{
// 输入
cout << "Enter worker's name: ";
getline(cin, fullname);
cout << "Enter worker's ID: ";
cin >> _id;
// 读取多余输入
while (cin.get() != '\n');
}
4. Person 类保存人的名和姓。除构造函数外,它还有Show()方法,用于显示名和姓。Gunslinger 类以 Person 类为虚基类派生而来,它包含一个Draw()成员,该方法返回一个 double 值,表示枪手拔枪的时间。这个类还包含一个 int 成员,表示手枪上的刻痕数。最后,这个类还包含一个 Show()函数,用于显示所有这些信息。PokerPlayer 类以 Person 类为虚基类派生而来。它包含一个 Draw()成员,该函数返回一个介于 1~52 的随机数,用于表示扑克牌的值(也可以定义一个 Card 类,其中包含花色和面值成员。 BadDude 类从 Gunslinger 和 PokerPlayer 类派生而来。它包含 Gdraw() 成员(返回枪手拔枪的时间)和 Cdraw()成员(返回下一张扑克牌),还有一个合适的 Show()函数。请定义这些类和方法及其他必要的方法(如设置对象值的方法),并使用一个类似于程序清单 14.12 的简单程序对它们进行测试。
答:基类的析构函数最好声明为虚函数。
BadDude.h 头文件
#pragma once
// 头文件
#include <iostream>
#include <string>
// using 声明
using std::string;
// Person 基类声明
class Person
{
private:
string f_name; // 名
string l_name; // 姓
public:
// 构造函数
Person(const string fname = "snow", const string lname = "like")
: f_name(fname), l_name(lname) { }
// 析构函数
virtual ~Person() { } // 基类的析构函数最好定义为虚函数
// 其他函数
virtual void Show() const;
};
// Gunslinger 派生类声明
class Gunslinger : virtual public Person
{
private:
int _num; // 刻痕数
public:
// 构造函数
Gunslinger() : Person(), _num(0) { }
Gunslinger(const string& fname, const string& lname, int num = 0)
: Person(fname, lname), _num(num) { }
Gunslinger(const Person& p, int num)
: Person(p), _num(num) { }
// 析构函数
virtual ~Gunslinger() { }
// 其他函数
virtual void Show() const;
double Draw() const; // 返回拔枪时间
};
// PokerPlayer 派生类声明
class PokerPlayer : virtual public Person
{
public:
// 构造函数
PokerPlayer() { }
PokerPlayer(const string& fname, const string& lname)
: Person(fname, lname) { }
// 析构函数
virtual ~PokerPlayer() { }
// 其他函数
int Draw() const; // 返回扑克牌的值
};
// BadDude 派生类声明
class BadDude : public Gunslinger, public PokerPlayer
{
public:
// 构造函数
BadDude();
BadDude(const string& fname, const string& lname, int num)
: Person(fname, lname), Gunslinger(fname, lname, num), PokerPlayer(fname, lname) { }
// 析构函数
virtual ~BadDude() { }
// 其他函数
virtual void Show() const;
double Gdraw() const; // 返回拔枪手拔枪时间
int Cdraw() const; // 返回下一张扑克牌
};
test4.cpp 测试文件
// 头文件
#include "BadDude.h"
// using 声明
using std::cout;
using std::endl;
int main()
{
Person snow;
snow.Show();
cout << endl;
Gunslinger G1("Snow", "White", 11);
G1.Show();
cout << "Time: " << G1.Draw() << endl;
PokerPlayer P1("Ship", "White");
P1.Show();
cout << P1.Draw();
return 0;
}
BadDude.cpp 方法定义文件
// 头文件
#include "BadDude.h"
#include <ctime>
#include <cstdlib>
// using 声明
using std::cout;
using std::endl;
// Person 基类方法定义
void Person::Show() const
{
cout << l_name << " " << f_name << endl;
}
// Gunslinger 派生类方法定义
double Gunslinger::Draw() const // 返回拔枪时间
{
// 设置随机数种子
srand((unsigned)time(0));
return rand() % 60;
}
void Gunslinger::Show() const
{
// 调用基类的 Show()函数
Person::Show();
// 显示新增变量
cout << "Nick: " << _num << endl;
}
// PokerPlayer 派生类方法定义
int PokerPlayer::Draw() const // 返回扑克牌的值
{
// 设置随机数种子
srand((unsigned)time(0));
return rand() % 52 + 1; // 返回扑克牌的值
}
// BadDude 派生类声明
void BadDude::Show() const
{
Gunslinger::Show();
}
double BadDude::Gdraw() const // 返回拔枪手拔枪时间
{
return Gunslinger::Draw();
}
int BadDude::Cdraw() const // 返回下一张扑克牌
{
return PokerPlayer::Draw();
}
5. 下面是一些类声明。
#pragma once
// 头文件
#include <iostream>
#include <string>
// using 声明
using std::string;
using std::ostream;
// abstr_emp 抽象基类声明
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 ~abstr_emp() = 0 { }
// 其他函数
virtual void ShowAll() const;
virtual void SetAll();
friend ostream& operator<<(ostream& os, const abstr_emp& e);
};
// employee 派生类声明
class employee : public abstr_emp
{
public:
// 构造函数
employee();
employee(const string& fn, const string& ln, const string& j);
// 析构函数
virtual ~employee() { }
// 其他函数
virtual void ShowAll() const;
virtual void SetAll();
};
// manager 派生类声明
class manager : virtual public abstr_emp
{
private:
int inchargeof;
protected:
int InChargeOf() const { return inchargeof; }
int& InCargeOf() { 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 ~manager() { }
// 其他函数
virtual void ShowAll() const;
virtual void SetAll();
};
// fink 派生类声明
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 ~fink() { }
// 其他函数
virtual void ShowAll() const;
virtual void SetAll();
};
// hignfink 派生类声明
class hignfink : public manager, public fink
{
public:
// 构造函数
hignfink();
hignfink(const string& fn, const string& ln, const string& j,
const string& rpo, int ico);
hignfink(const abstr_emp& e, const string& rpo, int ico);
hignfink(const fink& f, int ico);
hignfink(const manager& m, const string& rpo);
hignfink(const hignfink& h);
// 其他函数
virtual void ShowAll() const;
virtual void SetAll();
};
注意,该类层次结构使用了带虚基类的 MI,所以要牢记这种情况下用于构造函数初始化列表的特殊规则。还需注意的是,有些方法被声明为受保护的。这可以简化一些 hignfink 方法的代码(例如,如果 hignfink::ShowAll()只调用 fink::Show()和 manager::ShowAll(),则它将调用 abstr_emp::ShowAll()两次)。请提供类方法的实现,并在一个程序中对这些类进行测试。下面是一个小型测试程序。
// 头文件
#include "hignfink.h"
// using 声明
using std::endl;
using std::cout;
using std::cin;
int main()
{
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();
hignfink hf(ma, "Curly Kew");
hf.ShowAll();
cout << "Press a key for next phase:\n";
cin.get();
hignfink 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;
return 0;
}
**a. 为什么没有定义赋值运算符?
b. 为什么要将 Show()和 SetAll()定义为虚的?
c. 为什么要将 abstr_emp 定义为虚基类?
d. 为什么 hignfink 没有数据部分?
e. 为什么只需要一个 operator<<()版本?
f. 如果使用下面的程序代码替换程序的结尾部分将会发生声明情况?
abstr_emp tri[4] = {em, fi, hf, hf2};
for (int i = 0; i < 4; ++i)
{
tri[i].ShowAll();
}
答:
a. 所有类均不涉及动态内存存储,所以可以直接使用编译器提供的默认赋值运算符。
b. 派生类均重写了这两个函数,使用虚函数当使用基类指针来指向派生类时,调用该函数可以调用派生类的该函数,若不使用虚函数,则调用的时基类的该函数。
c. 因为 hignfink 类继承两个类,且该两个类的基类均为 abstr_emp,为了避免继承 abstr_emp类的成员两次,所以需要定义为虚基类。
d. 因为 hignfink类继承的成员够用,不需要新增数据部分。
e. 友元函数无法继承,且ShowAll()函数通过继承实现了相关功能,也就没必要再定义该函数。
f. 该代码的编译将不会通过,因为抽象基类不允许创建对象,而该代码创建了 4个对象。
hignfink.cpp 方法定义文件
// 头文件
#include "hignfink.h"
// using 声明
using std::cout;
using std::endl;
using std::cin;
// abstr_emp 抽象基类方法定义
// 构造函数
abstr_emp::abstr_emp() : fname("学生"), lname("大"), job("无")
{
}
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 << lname << " " << fname << endl;
cout << "Job: " << job << endl;
}
void abstr_emp::SetAll()
{
cout << "Enter first name: ";
getline(cin, fname);
cout << "Enter last name: ";
getline(cin, lname);
cout << "Enter the job: ";
getline(cin, job);
}
ostream& operator<<(ostream& os, const abstr_emp& e)
{
e.ShowAll();
return os;
}
// employee 派生类方法定义
// 构造函数
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 派生类方法定义
// 构造函数
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 << "Inchargeof: " << inchargeof << endl;
}
void manager::SetAll()
{
abstr_emp::SetAll();
cout << "Enter the inchargeof: ";
cin >> inchargeof;
// 清除多余输入
while (cin.get() != '\n');
}
// fink 派生类方法定义
// 构造函数
fink::fink()
: abstr_emp(), reportsto("")
{
}
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 << "Enter the reportsto: ";
getline(cin, reportsto);
}
// hignfink 派生类方法定义
// 构造函数
hignfink::hignfink()
: abstr_emp(), manager(), fink()
{
}
hignfink::hignfink(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)
{
}
hignfink::hignfink(const abstr_emp& e, const string& rpo, int ico)
: abstr_emp(e), manager(e, ico), fink(e, rpo)
{
}
hignfink::hignfink(const fink& f, int ico)
: abstr_emp(f), manager(f, ico), fink(f)
{
}
hignfink::hignfink(const manager& m, const string& rpo)
: abstr_emp(m), manager(m), fink(m, rpo)
{
}
hignfink::hignfink(const hignfink& h)
: abstr_emp(h), manager(h), fink(h)
{
}
// 其他函数
void hignfink::ShowAll() const
{
manager::ShowAll();
cout << "Reportsto: " << ReportsTo() << endl;
cout << endl;
}
void hignfink::SetAll()
{
manager::SetAll();
cout << "Enter the reportsto: ";
getline(cin, ReportsTo());
}
运行结果: