C++ Primer Plus 第十三章答案 类继承
程序员文章站
2023-12-22 23:10:04
...
复习题
//13.10
//1
基类的公有成员成为派生类的公有成员。基类的保护成员成为派生类的保护成员。
基类的私有成员被继承,但不能直接访问。
//2
不能继承构造函数,析构函数,赋值运算符和友元。
//3
返回类型为void则可以使用单个赋值而不能使用连锁赋值。
如果返回对象而不是引用,则该方法的执行速度变慢,因为返回语句需要复制对象。
//4
按派生的顺序调用构造函数,最早的构造函数最先调用,调用析构函数的顺序正好相反。
//5
需要,每个类都需要自己的构造函数,如果派生类没有添加新成员,构造函数可以为空但必须存在。
//6
只调用派生类方法,它取代基类定义,只有当派生类没有重新定义方法或使用作用域解析运算符时
才会调用基类方法,然而,应该把所有要重新定义的函数声明为虚函数。
//7
派生类构造函数使用new或new[]来初始化类的指针成员,则应该定义一个赋值运算符
更普遍地说,如果对于派生类成员默认赋值不正确,则应该定义赋值运算符。
//8
派生类对象的地址可以赋给基类指针,但只有通过显式类型转换才可以把基类的地址赋给派生类
指针,而使用这样的指针不一定安全。
//9
可以把派生类对象赋给基类对象,派生类中新增的数据成员都不会传递给基类对象,使用基类的
赋值运算符。但是仅当派生类定义了转换运算符(即包含将基类引用作为唯一参数的构造函数)
或使用基类为参数的赋值运算符时,相反方向的赋值才有可能。
//10
C++允许基类引用指向该基类派生来的任何类型。
//11
按值传递对象将调用复制构造函数。由于形参是基类对象,因此调用基类的复制构造函数。复制
构造函数以基类引用为参数,可以接受作为参数传递的派生类对象。最终结果是生成一个新的
基类对象,其成员对应于派生对象的基类部分。
//12
按引用传递可以确保函数从虚函数受益,另外,按引用传递可以节省内存和时间,尤其对于大型对象
按值传递的主要优点在于可以保护原始数据,但可以通过将引用作为const类型传递达到同样的目的。
//13
a,调用基类方法
b,调用派生类方法
//14
首先,这种类型不符合is-a模型,因此公有继承不适用。
其次House的area()定义隐藏了area()的Kitchen版本,不管这两个方法的特征标是否相同。
ps:只要基类定义了virtual,继承类的该函数也具有virtual属性。
practice 1
//classic.h
#pragma once
#include<string>
class Cd {
char performers[50];
char label[20];
int selections;
double playtime;
public:
Cd(const char* s1, const char* s2, int n, double x);
Cd();
virtual ~Cd();
virtual void Report()const;
};
class Classic :public Cd {
std::string works;
public:
Classic(const std::string& s, const char* s1, const char* s2, int n, double x);
Classic();
virtual ~Classic();
virtual void Report()const;
Classic& operator=(const Classic& cl);
};
//classic.cpp
#include<iostream>
#include"classic.h"
using std::cout;
using std::endl;
Cd::Cd(const char* s1, const char* s2, int n, double x) : selections(n), playtime(x) {
strcpy_s(performers, s1);
strcpy_s(label, s2);
}
Cd::Cd() :performers("null"), label("null"), selections(0), playtime(0) {}
Cd::~Cd() {}
void Cd::Report()const {
cout << "performers: " << performers << endl;
cout << "label: " << label << endl;
cout << "selections: " << selections << endl;
cout << "playtime: " << playtime << endl;
}
Classic::Classic(const std::string& s, const char* s1, const char* s2, int n, double x) :
works(s), Cd(s1, s2, n, x) {}
Classic::Classic() : works("null"), Cd() {}
Classic::~Classic() {}
void Classic::Report()const {
cout << "works: " << works << endl;
Cd::Report();
}
Classic& Classic::operator=(const Classic& cl) {
if (this == &cl)
return *this;
Cd::operator=(cl);
works = cl.works;
return *this;
}
//main.cpp
#include<iostream>
#include"classic.h"
using namespace std;
void Bravo(const Cd& disk) {
disk.Report();
}
int main() {
Cd c1("Beatles", "Capitol", 14, 35.5);
Classic c2 = Classic("pianosonata in B flat, Fantasia in C",
"Alfred Brendel", "Philips", 2, 57.17);
Cd* pcd = &c1;
cout << "Using object directly:\n";
c1.Report();
cout << endl;
c2.Report();
cout << endl << endl;
cout << "Using type cd* pointer to objects:\n";
pcd->Report();
pcd = &c2;
cout << endl;
pcd->Report();
cout << endl << endl;
cout << "Calling a function with a Cd reference argument:\n";
Bravo(c1);
cout << endl;
Bravo(c2);
cout << endl << endl;
cout << "Testing assignment:\n";
Classic copy;
copy = c2;
copy.Report();
return 0;
}
practcie 2
只改变实现不改变接口,因此main.cpp文件不用更改
//classic.h
#pragma once
class Cd {
char* performers;
char* label;
int selections;
double playtime;
public:
Cd(const char* s1, const char* s2, int n, double x);
Cd(const Cd& c);
Cd();
virtual ~Cd();
virtual void Report()const;
Cd& operator=(const Cd& c);
};
class Classic :public Cd {
char* works;
public:
Classic(const char* s, const char* s1, const char* s2, int n, double x);
Classic(const Classic& cl);
Classic();
virtual ~Classic();
virtual void Report()const;
Classic& operator=(const Classic& cl);
};
//classic.cpp
#include<iostream>
#include"classic.h"
#include<string>
#pragma warning(disable :4996)
using std::cout;
using std::endl;
using std::strcpy;
using std::strlen;
Cd::Cd(const char* s1, const char* s2, int n, double x) : selections(n), playtime(x) {
performers = new char[strlen(s1) + 1];
strcpy(performers, s1);
label = new char[strlen(s2) + 1];
strcpy(label, s2);
}
Cd::Cd(const Cd& c) {
performers = new char[strlen(c.performers) + 1];
strcpy(performers, c.performers);
label = new char[strlen(c.label) + 1];
strcpy(label, c.label);
selections = c.selections;
playtime = c.playtime;
}
Cd::Cd() :selections(0), playtime(0) {
performers = nullptr;
label = nullptr;
}
Cd::~Cd() {
delete[]performers;
delete[]label;
}
void Cd::Report()const {
cout << "performers: " << performers << endl;
cout << "label: " << label << endl;
cout << "selections: " << selections << endl;
cout << "playtime: " << playtime << endl;
}
Cd& Cd::operator=(const Cd& c) {
delete[]performers;
delete[]label;
performers = new char[strlen(c.performers) + 1];
label = new char[strlen(c.label) + 1];
strcpy(performers, c.performers);
strcpy(label, c.label);
selections = c.selections;
playtime = c.playtime;
return *this;
}
Classic::Classic(const char* s, const char* s1, const char* s2, int n, double x) :
Cd(s1, s2, n, x) {
works = new char[strlen(s) + 1];
strcpy(works, s);
}
Classic::Classic(const Classic& cl) : Cd(cl) {
works = new char[strlen(cl.works) + 1];
strcpy(works,cl.works);
}
Classic::Classic() : works(nullptr), Cd() {}
Classic::~Classic() {
delete[]works;
}
void Classic::Report()const {
cout << "works: " << works << endl;
Cd::Report();
}
Classic& Classic::operator=(const Classic& cl) {
if (this == &cl)
return *this;
Cd::operator=(cl);
delete[]works;
works = new char[strlen(cl.works) + 1];
strcpy(works, cl.works);
return *this;
}
practice 3
这题有一个巨坑,书上的13.10有一个continue用来清除输入,但是书上的程序是读取的数字,用的cin,而练习题要用cin.getline(),而且由于前面用的cin读取1,2,3判断创建哪一种类,需要一个cin.get()读取换行符。这时候巨坑出现了,如果按书上的程序抄的同时发现没有用cin.get()导致color和style读不进去然后加上cin.get(),我换了七八个位置加cin.get(),能解决问题,但是会要多读一个换行符才能进去下一个循环,我这个憨批花了两个小时一直没发现问题,以为是cin.get()的问题,现在想想就是因为有个while——continue那个循环,必须读一个字符,所以要多输入一个换行符,现在人已经被自己气死了。。
//dma.h
#pragma once
#include<iostream>
class base {
char* label;
int rating;
public:
base(const char* l = "null", int r = 0);
base(const base& b);
virtual ~base() = 0;
base& operator=(const base& b);
virtual void View() = 0;
};
/
class baseDMA :public base {
public:
baseDMA(const char* l = "null", int r = 0);
virtual ~baseDMA();
virtual void View();
};
//
class lacksDMA :public base {
enum { COL_LEN = 40 };
char color[COL_LEN];
public:
lacksDMA(const char* c = "blank", const char* l = "null", int r = 0);
lacksDMA(const char* c, const base& b);
virtual ~lacksDMA();
virtual void View();
};
//
class hasDMA :public base {
char* style;
public:
hasDMA(const char* s = "none", const char* l = "null", int r = 0);
hasDMA(const char* s, const base& b);
hasDMA(const hasDMA& hd);
virtual ~hasDMA();
hasDMA& operator=(hasDMA& hd);
virtual void View();
};
//dma.cpp
#include"dma.h"
#pragma warning(disable :4996)
using std::cout;
using std::endl;
base::base(const char* l, int r) :rating(r) {
label = new char[std::strlen(l) + 1];
strcpy(label, l);
}
base::base(const base& b) :rating(b.rating) {
label = new char[std::strlen(b.label) + 1];
strcpy(label, b.label);
}
base::~base() {
delete[]label;
}
base& base::operator=(const base& b) {
if (this == &b)
return *this;
delete[]label;
label = new char[std::strlen(b.label) + 1];
strcpy(label, b.label);
rating = b.rating;
return *this;
}
void base::View() {
cout << "label: " << label << endl;
cout << "rating: " << rating << endl;
}
/
baseDMA::baseDMA(const char* l, int r) :base(l, r) {}
baseDMA::~baseDMA() {}
void baseDMA::View() { base::View(); }
/
lacksDMA::lacksDMA(const char* c, const char* l, int r) : base(l, r) {
strcpy_s(color, c);
}
lacksDMA::lacksDMA(const char* c, const base& b) : base(b) {
std::strcpy(color, c);
}
lacksDMA::~lacksDMA() {}
void lacksDMA::View() {
base::View();
cout << "color: " << color << endl;
}
/
hasDMA::hasDMA(const char* s, const char* l, int r) :base(l, r) {
style = new char[std::strlen(s) + 1];
strcpy_s(style, std::strlen(s) + 1, s);
}
hasDMA::hasDMA(const char* s, const base& b) : base(b) {
style = new char[std::strlen(s) + 1];
strcpy_s(style, std::strlen(s) + 1, s);
}
hasDMA::hasDMA(const hasDMA& hd) : base(hd) {
style=new char[std::strlen(hd.style) + 1];
strcpy_s(style, std::strlen(hd.style) + 1, hd.style);
}
hasDMA::~hasDMA() {
delete[]style;
}
hasDMA& hasDMA::operator=(hasDMA& hd) {
if (this == &hd)
return *this;
base::operator=(hd);
delete[]style;
style = new char[std::strlen(hd.style) + 1];
strcpy_s(style, std::strlen(hd.style) + 1, hd.style);
return *this;
}
void hasDMA::View() {
base::View();
cout << "style: " << style << endl;
}
//main.cpp
#include<iostream>
#include"dma.h"
using namespace std;
int main() {
base* pbase[4];
char label[40];
int rating = 0;
char color[40];
char style[40];
char kind;
for (int i = 0; i < 4; i++) {
cout << "Enter label:";
cin.getline(label, 40);
cout << "Enter rating:";
cin >> rating;
cout << "Enter 1 for baseDMA, 2 for lacksDMA, 3 for hasDMA:";
while (cin >> kind && (kind != '1' && kind != '2' && kind != '3'))
cout << "Enter either 1,2 or 3:";
cin.get();
if (kind == '1')
pbase[i] = new baseDMA(label, rating);
else if (kind == '2') {
cout << "Enter color:";
cin.getline(color, 40);
pbase[i] = new lacksDMA(color, label, rating);
}
else {
cout << "Enter style:";
cin.getline(style, 40);
pbase[i] = new hasDMA(style, label, rating);
}
}
cout << endl;
for (int i = 0; i < 4; i++) {
pbase[i]->View();
cout << endl;
}
for (int i = 0; i < 4; i++)
delete pbase[i];
cout << "Done!\n";
return 0;
}
practice 4
c.赋值运算符和友元函数是无法继承的,也不需要重新定义,而是每个类各自编写,
每个类都只会使用自己的operator=()(赋值运算符)和operator<<()(友元函
数),因此也就不需要声明为虚函数
b.首先构造函数和Show()因为有了新成员需要重新定义,析构函数,复制构造函数,
赋值运算符因为使用了char*指针,需要用new[]分配内存,所以三者都要重新定义,
+=和-=运算符重载,它们对两个类的行为都是一样的,不需要重新定义
//port.h
#pragma once
#include<iostream>
using namespace std;
class Port {
char* brand;
char style[20];
int bottles;
public:
Port(const char* br = "none", const char* st = "none", int b = 0);
Port(const Port& p);
virtual~Port() { delete[]brand; }
Port& operator=(const Port& p);
Port& operator+=(int b);
Port& operator-=(int b);
int BattleCount() { return bottles; }
virtual void Show()const;
friend ostream& operator<<(ostream& os, const Port& p);
};
class VintagePort :public Port {
char* nickname;
int year;
public:
VintagePort();
VintagePort(const char* br, const char* st, int b, const char* nn, int y);
VintagePort(const VintagePort& vp);
~VintagePort() { delete[]nickname; }
VintagePort& operator=(const VintagePort& vp);
void Show()const;
friend ostream& operator<<(ostream& os, const VintagePort& vp);
};
//port.cpp
#include"port.h"
Port::Port(const char* br = "none", const char* st = "none", int b = 0) : bottles(b) {
brand = new char[strlen(br) + 1];
strcpy_s(brand, strlen(br) + 1, br);
strcpy_s(style, st);
}
Port::Port(const Port& p) {
brand = new char[strlen(p.brand) + 1];
strcpy_s(brand, strlen(p.brand) + 1, p.brand);
strcpy_s(style, p.style);
bottles = p.bottles;
}
Port& Port::operator=(const Port& p) {
if (this == &p)
return *this;
delete[]brand;
strcpy_s(brand, strlen(p.brand) + 1, p.brand);
strcpy_s(style, p.style);
bottles = p.bottles;
}
Port& Port::operator+=(int b) {
bottles += b;
return *this;
}
Port& Port::operator-=(int b) {
bottles -= b;
return *this;
}
void Port::Show()const {
cout << "Brand: " << brand << endl;
cout << "Style: " << style << endl;
cout << "Bottles: " << bottles << endl;
}
ostream& operator<<(ostream& os, const Port& p) {
os << p.brand << " " << p.style << " " << p.bottles << " ";
}
VintagePort::VintagePort() :Port() {
nickname = nullptr;
year = 0;
}
VintagePort::VintagePort(const char* br, const char* st, int b, const char* nn, int y)
: Port(br, st, b), year(y) {
nickname = new char[strlen(nn) + 1];
strcpy_s(nickname, strlen(nn) + 1, nn);
}
VintagePort::VintagePort(const VintagePort& vp) : Port(vp), year(vp.year) {
nickname = new char[strlen(vp.nickname) + 1];
strcpy_s(nickname, strlen(vp.nickname) + 1, vp.nickname);
}
VintagePort& VintagePort::operator=(const VintagePort& vp) {
if (this == &vp)
return *this;
Port::operator=(vp);
delete[]nickname;
nickname = new char[strlen(vp.nickname) + 1];
strcpy_s(nickname, strlen(vp.nickname) + 1, vp.nickname);
year = vp.year;
return *this;
}
void VintagePort::Show()const {
Port::Show();
cout << "Nickname: " << nickname << endl;
cout << "Year: " << year << endl;
}
ostream& operator<<(ostream& os, const VintagePort& vp) {
os << (const Port&)vp;
os << vp.nickname << " " << vp.year << " ";
}