C++ Primer Plus(第六版)第十四章课后编程题加讲解
第一题
第一题与第二题其实都是一个has-a 关系的温习 无非第一题是包含 第二题是私有继承 第一题比较简单 没有什么说的
PS:值得注意的是如果代码重出现了比较繁琐的模板类 我们最好能使用typedef去简化
wine.h
//pair类的创建
#ifndef WINE_H_
#define WINE_H_
#include<string>
#include<iostream>
#include<valarray>
using std::pair;
using std::string;
using std::cin;
using std::cout;
using std::endl;
class Wine
{
private:
typedef std::valarray<int> Arrayint;
typedef pair<Arrayint,Arrayint> pairArray;
string name;
pairArray data; //包含
int number;
public:
Wine(const char * s,int n,const int yr[],const int bot[]);
Wine(const char *s,int n);
void Getbottles();
string & Label() {return name;}
int sum() const;
void show() const;
};
Wine::Wine(const char *s,int n,const int yr[],const int bot[])
{
name = s;
number = n;
Arrayint a(n);
Arrayint b(n);
for(int i=0;i<n;i++)
{
a[i]=yr[i];
b[i]=bot[i];
}
data=std::make_pair(a,b);
}
Wine::Wine(const char *s,int n)
{
name = s;
number = n;
Arrayint a(n);
Arrayint b(n);
data = std::make_pair(a,b);
}
void Wine::Getbottles()
{
cout << "enter " << name << "data for " << number << "year(s)\n";
int i;
for(i=0;i<number;i++)
{
cout << "Enter year:";
cin >> data.first[i];
cout << "Enter bottles for that year:";
cin >> data.second[i];
}
}
void Wine::show() const
{
cout << "Wine::" << name << endl;
cout << " year bottles\n";
for(int i=0;i<number;i++)
{
cout << " "<<data.first[i]<<" "<<data.second[i]<<endl;
}
}
int Wine::sum() const
{
int sum2=0;
for(int i=0;i<number;i++)
{
sum2+=data.second[i];
}
return sum2;
}
#endif
wine.cpp
#include"Wine.h"
#include<iostream>
int main()
{
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 year:";
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;
}
第二题
私有继承是我感觉代码很容易出现错误 因为其中我们为了使用对象要使用强制类型转换 首先我希望大家看一个简单的代码
#include<iostream>
using namespace std;
int main()
{
int a=5;
(int &)a=6;
cout << a << endl;
(int)a=7;
cout << a << endl;
return 0;
}
这个代码是不可以通过编译的 原因是因为 (int)a=7; 这一句是错误的 但为什么(int&)a=7 是正确的呢 a原本是一个右值 (int&)之后变成了左值 这不难理解 因为a被强制转换为自己的引用 而(int)a还是右值
这个代码为了让大家小心使用私有继承 因为其中大量用到了强制类型转换 这道题我在写的时候出现了一个bug 真的找了很久 最后发现是强制类型转换没有加&
wine2.h
#ifndef WINE2_H_
#define WINE2_H_
#include<iostream>
#include<string>
#include<valarray>
using std::pair;
using std::string;
using std::cin;
using std::cout;
using std::endl;
using std::string;
typedef std::valarray<int> Arrayint;
typedef pair<Arrayint,Arrayint> pairArray;
class Wine2 :private string,private pairArray
{
private:
int number;
public:
Wine2() {};
Wine2(const char *,int,const int yr[],const int bot[]);
Wine2(const char *,int);
void Getbottles();
string & Label();
int sum() const;
void show() const;
};
Wine2::Wine2(const char *s,int n,const int yr[],const int bot[])
:string(s),number(n),pairArray(Arrayint(yr,n),Arrayint(bot,n))
{}
Wine2::Wine2(const char *s,int n)
:string(s),number(n)
{}
void Wine2::Getbottles()
{
Arrayint a(number);
Arrayint b(number);
cout << "enter " << (const string &)*this << "data for " << number << "year(s)\n";
int i;
for(i=0;i<number;i++)
{
cout << "Enter year:";
cin >> a[i];
cout << "Enter bottles for that year:";
cin >> b[i];
}
(pairArray &)*this=std::make_pair(a,b);
//这里刚开始没有加& 在81行出现段错误 这个bug找了很久,,
//大家引以为戒 注意强制转换加&与不加是不一样的 加上是对本身做改变
//不加就是创建一个临时对象 对其做操作
}
string & Wine2::Label()
{
return (string &)*this;
}
int Wine2::sum() const
{
int sum_all=0;
for(int i=0;i<number;i++)
{
//sum_all+=(pairArray)*this.second[i];
sum_all+=pairArray::second[i];
//在这里pairArray相当于一个类 second相当于一个方法
//使用作用域解析运算符
}
return sum_all;
}
/* typedef std::valarray<int> Arrayint;
typedef pair<Arrayint,Arrayint> pairArray; */
void Wine2::show() const
{
cout << "Wine::" << (string)*this << endl;
cout << " year bottles\n";
for(int i=0;i<number;i++)
{
cout << " "<<pairArray::first[i]<<" " << pairArray::second[i]<< endl;
}
}
#endif
//需要注意的地方要使用某个对象要进行强制类型转换 例:string
//要调用函数需要进行作用域解析运算符 例:first
wine2.cpp
#include"Wine2.h"
#include<iostream>
//其实这部分与第一道题相同 因为接口相同
int main()
{
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 year:";
int yrs;
cin >> yrs; //输入阶段
Wine2 holding(lab,yrs);
holding.Getbottles();
holding.show();
const int YRS=3;
int y[YRS]={1993,1995,1998};
int b[YRS]={48,60,72};
Wine2 more("Gushing Grape Red",YRS,y,b);
more.show();
cout <<"Total bottles for "<< more.Label()
<<": " << more.sum() << endl;
cout << "Bye!\n";
return 0;
}
第三题
首先我们可能在看到这道题的时候有点懵逼 创建一个指向worker的指针队列?什么鬼 但仔细的同学应该知道572页的深入探讨模板类中就有提到指针栈 与指针队列一个意思 旨在告诉我们当模板元素为指针的时候 我们需要注意的点 就是在类中用一个指针数组去存放所接受到的元素 然后又因为worker是一个虚基类 这道题其实意思就很明确了 就是想让我们利用基类指针的兼容性在指向worker指针的队列中储存其派生类的指针 然后就是代码工作了 需要注意的是队列中push操作的写法 与栈并不一样
先是书上关于worker类的代码 如果没有在学习MI的时候敲过建议自己尝试实现 因为后面还有两道MI的题目
*PS :
因为我一般的习惯是把类的实现与声明分别放在两个文件中 这样可以更加清楚 但这道题中模板类确实写在了一起 为什么呢 因为模板不是函数 并不可以单独编译
worker.h
#ifndef WORKERMI_H_
#define WORKERMI_H_
#include<string>
using std::string;
class worker
{
private:
string fullname;
long id;
protected:
virtual void data() const;
virtual void get();
public:
worker():fullname("Null name"),id(0L){}
worker(const string &s,long n):fullname(s),id(n){}
virtual ~worker() = 0;
virtual void set() = 0;
virtual void show()const = 0;
};
class waiter : virtual public worker//也是公有继承
{
private:
int panache;
protected:
void data()const;//输出数据
void get();//得到数据
public:
waiter():worker(),panache(0){}
waiter(const string &s,long n,int p=0):
worker(s,n),panache(p){}
waiter(const worker & wk,int p=0):
worker(wk),panache(p){}
void set();
void show() const;
};
class singer : virtual public worker
{
protected:
void data() const;
void get();
enum{other,alto,contralto,sprano,bass,baritone,teror};
enum{Vtypes = 7};
private:
static char *pv[Vtypes]; //存储字符串
int voice;
public:
singer() : worker(),voice(0){}
singer(const string &s,long n,int v=0):
worker(s,n),voice(v){}
singer(const worker & wk,int v=0):
worker(wk),voice(v){}
void set();
void show() const;
};
class singingwaiter :public singer,public waiter
{
protected:
void data() const;
void get();
public:
singingwaiter(){}
//使用默认构造函数 因为没有私有成员 不用初始化
singingwaiter(const string &s,long n,int p=0,int v=0):
worker(s,n),waiter(s,n,p),singer(s,n,v){}
singingwaiter(const worker & wk,int p=0,int v=0):
worker(wk),waiter(wk,p),singer(wk,v){}
singingwaiter(const waiter & wt,int v=0):
worker(wt),waiter(wt),singer(wt,v){}
singingwaiter(const singer & wt,int p=0):
worker(wt),waiter(wt,p),singer(wt){}
void set();
void show() const;
};
#endif
worker.hpp
#include"workermi.h"
#include<iostream>
using std::cout;
using std::cin;
using std::endl;
worker::~worker(){}
void worker::data()const
{
cout << "name:" << fullname << endl;
cout << "id:" << id << endl;
}
void worker::get()
{
getline(cin,fullname);
cout <<"please enter id:\n";
cin >> id;
}
void waiter::get()
{
cout <<"enter panache:\n";
cin >> panache;
while(cin.get() != '\n')
continue;
}
void waiter::data()const
{
cout << "panache :\n" << panache <<endl;
}
void waiter::set()
{
cout <<"please enter fullname:\n";
worker::get();
get();
}
void waiter::show() const
{
cout <<"Category:waiter:\n";
worker::data();
data();
}
char * singer::pv[singer::Vtypes] =
{
"other","alto","contralta","sprano",
"bass","baritone","teror"
};
void singer::data()const
{
cout << "voice:\n" << voice << endl;
}
void singer::get()
{
cout << "enter the number of singer's range\n";
for(int i=0;i<Vtypes;i++)
{
cout << i <<":"<< pv[i] << endl;
}
cin >> voice;
while(cin.get()!='\n')
continue;
}
void singer::set()
{
cout << "please enter fullname:\n";
worker::get();
get();
}
void singer::show()const
{
cout <<"category singer:\n";
worker::data();
data();
}
void singingwaiter::data()const
{
singer::data();
waiter::data();
}
void singingwaiter::get()
{
singer::get();
waiter::get();
}
void singingwaiter::set()
{
cout << "enter fullname:\n";
worker::get();
get();
}
void singingwaiter::show()const
{
cout << "category:singingwaiter:\n";
worker::data();
data();
}
QueueTp.h
#ifndef QUEUETP_H_
#define QUEUETP_H_
#include<iostream>
#include<string>
template<typename T>
class QueueTp
{
private:
const int LEN=10;
T *data;
int top;
public:
QueueTp(){top=0,data=new T[LEN];}
QueueTp(int t){top=0,data=new T[t];}
~QueueTp() {delete []data;}
bool is_empty()const {return top==0;}
bool is_full() const {return top==LEN;}
int size(){return top;}
bool push(T item);
bool pop();
T& front()const;
T& rear()const;
};
#endif
template<typename T>
bool QueueTp<T>::push(T item)
{
if(is_full())
{
std::cout <<"Stack has full!\n";
return false;
}else
{
for(int i=top;i>0;i--)
{
data[i]=data[i-1];
}
top++;
data[0]=item;
return true;
}
}
template<typename T>
bool QueueTp<T>::pop()
{
if(is_empty())
{
std::cout << "Stack has empty!\n";
}else
{
top--;
return true;
}
}
template <typename T>
T &QueueTp<T>::front() const
{
return data[top - 1];
}
template <typename T>
T &QueueTp<T>::rear() const
{
return data[0];
}
QueueTp.cpp
#include<iostream>
#include<cstring>
#include"QueueTp.h"
#include"workermi.h"
#include"workermi.hpp"
using std::cin;
using std::cout;
using std::endl;
using std::string;
const int len=10;
QueueTp<worker *> que(len);
int main()
{
char ch;
for(int i=0;i<len;i++)
{
cout <<"w:waiter s:singer t:ingingwaiter\n"
<<"q to quit."<<endl;
cin >> ch;
while(std::strchr("wstq",ch)==NULL)
cout <<"please enter w s t q!\n";
if(ch=='q') break;
switch (ch)
{
case 'w':
que.push(new waiter);
break;
case 's':
que.push(new singer);
break;
case 't':
que.push(new singingwaiter);
break;
}
while(cin.get()!='\n')
cin.get();
que.rear()->set(); //传入的是指针
}
for(int i=0;i<que.size();i++)
{
que.front()->show();
que.push(que.front());
que.pop();
cout << endl;
}
for(int i=0;i<len;i++)
{
delete que.front();
que.pop();
}
return 0;
}
第四题
这道题考我们MI的知识 基本与书上例题相同 没有什么说的 分享一个在我写的过程中发现的一个有趣的bug 就是在下面代码的第19行如果去掉const限定符是无法通过编译的 这是为什么呢 因为在下面函数中调用基类中 data() 函数也是有const限定符的 如果基类中的data函数不加const限定符 编译器无法确定这个data函数是否会修改 如果修改 则与调用data函数的函数的const冲突 所以编译器会不通过 以前一直觉得const可要可不要(菜) 现在才知道const是非常重要的 在能用的地方一定要用 。
person.h
//虚基类的练习 需要注意的是19行的不可以不加const限定符 否则会无法通过编译
//原因是const无法调用非const类型
//对于虚函数 声明一定要一致 否则会无法通过编译 尤其注意const限定符
#ifndef PERSON_H_
#define PERSON_H_
#include<iostream>
#include<string>
using std::cin;
using std::cout;
using std::endl;
using std::string;
class person
{
private:
string name;
protected:
virtual void data()const {cout <<"name: " << name;}
public:
person() :name("No name") {}
person(string s1) :name(s1){}
person(char *s): name(s){}
virtual ~person(){}
virtual void show()const{data();};
};
class Gunslinger : virtual public person
{
private:
double time;
int scratch;
protected:
void data()const{
cout <<"time: " << time << endl;
cout <<"scratch: " << scratch << endl;
}
public:
Gunslinger():person(),time(0),scratch(0){}
Gunslinger(string s1,double t,int s)
:person(s1),time(t),scratch(s){}
Gunslinger(person & p,double t,int s)
:person(p),time(t),scratch(s){}
double Draw() const {return time;} //如果返回值为引用则可修改成员 题目没说 但最好不那么做
void show() const //这里是const
{
person::data();//这里也必须是const
data();
}
};
class Pokerplayer : virtual public person
{
public:
Pokerplayer() :person() {}
Pokerplayer(person & p):person(p) {}
Pokerplayer(string s):person(s) {}
int Draw() {return std::rand()%53+1;}
void show() const {person::data();}
};
class BadDude:public Pokerplayer,public Gunslinger
{
protected:
void data() const{
Gunslinger::data();
}
public:
BadDude():person(),Gunslinger() {}
BadDude(string s1,double t,int s)
:person(s1),Gunslinger(s1,t,s) {}
BadDude(person & p,double t,int s)
:person(p),Gunslinger(p,t,s) {}
int Gdraw() const{Pokerplayer::show();}
int Cdraw() {return std::rand()%53+1;}
void show()const{
data();
person::data();
}
};
#endif
一个简单的测试代码
person.cpp
#include"person.h"
#include<iostream>
int main()
{
person p("Tree");
Gunslinger g("Tree", 3, 1.2);
Pokerplayer po("shana");
BadDude b("yuuji", 2, 1.1);
person *ptr = &p;
ptr->show();
ptr = &g;
ptr->show();
ptr = &po;
ptr->show();
ptr = &b;
ptr->show();
return 0;
}
第五题
这道题考我们的还是关于MI的知识 题面与书上的例题也是几乎一样 但是也有不尽相同的地方 那就是在多个派生类中出现了复制构造函数和在参数列表中包含所继承来的基类 这该怎么写呢 答案就是用强制类型转换去把给我们的类去强制转换为我们需要的类 这样这道题就迎刃而解了。
代码中有些地方为了方便 写成了内联函数 与书上有些地方不同 .cpp文件大家可以不用手打 直接复制就好 其他建议手打 能够发现很多自身小细节上的问题 比如说函数声明上的const在写函数是忘掉 我经常犯这个错误。
emp.h
//这是plus第十四章课后第五题 关于MI的练习
#ifndef EMP_H_
#define EMP_H_
#include<iostream>
#include<string>
using std::cout;
using std::cin;
using std::endl;
using std::string;
using std::ostream;
class abstr_emp
{
private:
string fname;
string lname;
string job;
public:
abstr_emp(){}
abstr_emp(const string s1,const string s2,const string s3)
:fname(s1),lname(s2),job(s3){}
virtual void showAll() const;
virtual void setAll();
friend ostream &
operator<<(ostream & os,const abstr_emp & ab);
virtual ~abstr_emp() = 0;
};
class employee : public abstr_emp
{
public:
employee(){}
employee(const string &s1,const string &s2,const string &s3)
:abstr_emp(s1,s1,s3){}
virtual void showAll() const{abstr_emp::showAll();};
virtual void setAll() {abstr_emp::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 &s1,const string &s2,const string &s3,int in=0)
:abstr_emp(s1,s2,s3),inchargeof(in){}
manager(const abstr_emp & ab,int in=0)
:abstr_emp(ab),inchargeof(in){}
manager(const manager & man);
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 &s1,const string &s2,const string &s3,const string in)
:abstr_emp(s1,s2,s3),reportsto(in){}
fink(const abstr_emp & ab,string in)
:abstr_emp(ab),reportsto(in){}
fink(const fink & f);
virtual void showAll() const;
virtual void setAll();
};
class highfink: public manager, public fink
{
public:
highfink(){}
highfink(const string &s1,const string &s2,const string &s3,const string s,int in=0)
:abstr_emp(s1,s2,s3),manager(s1,s2,s3,in),fink(s1,s2,s3,s){}
highfink(abstr_emp & ab,const string &s,int in=0)
:abstr_emp(ab),manager(ab,in),fink(ab,s){}
highfink(const fink & f,int in=0);
highfink(const manager & m,const string &s);
highfink(const highfink & high);
virtual void showAll() const;
virtual void setAll();
};
#endif
emp.hpp
#include"emp.h"
#include<string>
#include<iostream>
using std::cout;
using std::cin;
using std::endl;
using std::string;
using std::ostream;
void abstr_emp::showAll()const
{
cout <<"fname: "<< fname<<endl;
cout <<"lname: "<< lname<<endl;
cout <<"job: "<< job <<endl;
}
void abstr_emp::setAll()
{
cout <<"please enter fname:";
cin >> fname;
cout <<"please enter lname:";
cin >> lname;
cout <<"please enter job:";
cin >> job;
}
ostream &operator<<(ostream & os,const abstr_emp &ab)
{
os << "fname:" << ab.fname<<endl;
os << "lname:" << ab.lname<<endl;
os << "job: " << ab.job <<endl;
}
abstr_emp::~abstr_emp(){}
manager::manager(const manager & man):abstr_emp((const abstr_emp &)man)
{
inchargeof=man.inchargeof;
}
void manager::showAll()const
{
abstr_emp::showAll();
cout <<"Inchargerof"<<InChargeof()<<endl;
}
void manager::setAll()
{
abstr_emp::setAll();
cout <<"please enter inchargeof:";
cin >> InChargeof();
}
fink::fink(const fink & f):abstr_emp((const abstr_emp &)f)
{
reportsto=f.reportsto;
}
void fink::showAll()const
{
abstr_emp::showAll();
cout<<"ReportsTo"<<ReportsTo()<<endl;
}
void fink::setAll()
{
abstr_emp::setAll();
cout << "please enter reportsto:";
cin >> ReportsTo();
}
void highfink::showAll() const
{
abstr_emp::showAll();
cout << ReportsTo();
cout << InChargeof();
}
void highfink::setAll()
{
abstr_emp::setAll();
cout <<"please enter inchargeof:";
cin >> InChargeof();
cout << "please enter reportsto:";
cin >> ReportsTo();
}
highfink::highfink(const manager & m,const string &s)
:abstr_emp((const abstr_emp &)m),manager(m),fink((const abstr_emp &)m,s){}
highfink::highfink(const fink & f,const int in)
:abstr_emp((const abstr_emp &)f),manager((const abstr_emp &)f,in),fink(f){}
highfink::highfink(const highfink & high):abstr_emp((const abstr_emp&)high),manager((const manager&)high),fink((const fink&)high){}
emp.cpp
#include"emp.h"
#include"emp.hpp"
#include<iostream>
#include<string>
using std::cout;
using std::cin;
using std::endl;
using std::string;
using std::ostream;
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", "Ogga", "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";
abstr_emp *tri[4] = { &em, &fi, &hf, &hf2 };
for (int i = 0; i < 4; ++i)
tri[i]->showAll();
return 0;
}