C++ Primer Plus 第十三章答案 类继承

简介: 只有聪明人才能看见的摘要~( ̄▽ ̄~)~

 复习题

//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属性。

image.gif

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;
}

image.gif

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;
}

image.gif

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;
}

image.gif

practice 4

c.赋值运算符和友元函数是无法继承的,也不需要重新定义,而是每个类各自编写,
每个类都只会使用自己的operator=()(赋值运算符)和operator<<()(友元函
数),因此也就不需要声明为虚函数
b.首先构造函数和Show()因为有了新成员需要重新定义,析构函数,复制构造函数,
赋值运算符因为使用了char*指针,需要用new[]分配内存,所以三者都要重新定义,
+=和-=运算符重载,它们对两个类的行为都是一样的,不需要重新定义

image.gif

//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 << " ";
}

image.gif


目录
相关文章
|
11天前
|
C++ 芯片
【C++面向对象——类与对象】Computer类(头歌实践教学平台习题)【合集】
声明一个简单的Computer类,含有数据成员芯片(cpu)、内存(ram)、光驱(cdrom)等等,以及两个公有成员函数run、stop。只能在类的内部访问。这是一种数据隐藏的机制,用于保护类的数据不被外部随意修改。根据提示,在右侧编辑器补充代码,平台会对你编写的代码进行测试。成员可以在派生类(继承该类的子类)中访问。成员,在类的外部不能直接访问。可以在类的外部直接访问。为了完成本关任务,你需要掌握。
51 18
|
11天前
|
存储 编译器 数据安全/隐私保护
【C++面向对象——类与对象】CPU类(头歌实践教学平台习题)【合集】
声明一个CPU类,包含等级(rank)、频率(frequency)、电压(voltage)等属性,以及两个公有成员函数run、stop。根据提示,在右侧编辑器补充代码,平台会对你编写的代码进行测试。​ 相关知识 类的声明和使用。 类的声明和对象的声明。 构造函数和析构函数的执行。 一、类的声明和使用 1.类的声明基础 在C++中,类是创建对象的蓝图。类的声明定义了类的成员,包括数据成员(变量)和成员函数(方法)。一个简单的类声明示例如下: classMyClass{ public: int
37 13
|
11天前
|
编译器 数据安全/隐私保护 C++
【C++面向对象——继承与派生】派生类的应用(头歌实践教学平台习题)【合集】
本实验旨在学习类的继承关系、不同继承方式下的访问控制及利用虚基类解决二义性问题。主要内容包括: 1. **类的继承关系基础概念**:介绍继承的定义及声明派生类的语法。 2. **不同继承方式下对基类成员的访问控制**:详细说明`public`、`private`和`protected`继承方式对基类成员的访问权限影响。 3. **利用虚基类解决二义性问题**:解释多继承中可能出现的二义性及其解决方案——虚基类。 实验任务要求从`people`类派生出`student`、`teacher`、`graduate`和`TA`类,添加特定属性并测试这些类的功能。最终通过创建教师和助教实例,验证代码
37 5
|
11天前
|
存储 算法 搜索推荐
【C++面向对象——群体类和群体数据的组织】实现含排序功能的数组类(头歌实践教学平台习题)【合集】
1. **相关排序和查找算法的原理**:介绍直接插入排序、直接选择排序、冒泡排序和顺序查找的基本原理及其实现代码。 2. **C++ 类与成员函数的定义**:讲解如何定义`Array`类,包括类的声明和实现,以及成员函数的定义与调用。 3. **数组作为类的成员变量的处理**:探讨内存管理和正确访问数组元素的方法,确保在类中正确使用动态分配的数组。 4. **函数参数传递与返回值处理**:解释排序和查找函数的参数传递方式及返回值处理,确保函数功能正确实现。 通过掌握这些知识,可以顺利地将排序和查找算法封装到`Array`类中,并进行测试验证。编程要求是在右侧编辑器补充代码以实现三种排序算法
27 5
|
11天前
|
Serverless 编译器 C++
【C++面向对象——类的多态性与虚函数】计算图像面积(头歌实践教学平台习题)【合集】
本任务要求设计一个矩形类、圆形类和图形基类,计算并输出相应图形面积。相关知识点包括纯虚函数和抽象类的使用。 **目录:** - 任务描述 - 相关知识 - 纯虚函数 - 特点 - 使用场景 - 作用 - 注意事项 - 相关概念对比 - 抽象类的使用 - 定义与概念 - 使用场景 - 编程要求 - 测试说明 - 通关代码 - 测试结果 **任务概述:** 1. **图形基类(Shape)**:包含纯虚函数 `void PrintArea()`。 2. **矩形类(Rectangle)**:继承 Shape 类,重写 `Print
32 4
|
11天前
|
设计模式 IDE 编译器
【C++面向对象——类的多态性与虚函数】编写教学游戏:认识动物(头歌实践教学平台习题)【合集】
本项目旨在通过C++编程实现一个教学游戏,帮助小朋友认识动物。程序设计了一个动物园场景,包含Dog、Bird和Frog三种动物。每个动物都有move和shout行为,用于展示其特征。游戏随机挑选10个动物,前5个供学习,后5个用于测试。使用虚函数和多态实现不同动物的行为,确保代码灵活扩展。此外,通过typeid获取对象类型,并利用strstr辅助判断类型。相关头文件如&lt;string&gt;、&lt;cstdlib&gt;等确保程序正常运行。最终,根据小朋友的回答计算得分,提供互动学习体验。 - **任务描述**:编写教学游戏,随机挑选10个动物进行展示与测试。 - **类设计**:基类
26 3
|
2月前
|
存储 编译器 C语言
【c++丨STL】string类的使用
本文介绍了C++中`string`类的基本概念及其主要接口。`string`类在C++标准库中扮演着重要角色,它提供了比C语言中字符串处理函数更丰富、安全和便捷的功能。文章详细讲解了`string`类的构造函数、赋值运算符、容量管理接口、元素访问及遍历方法、字符串修改操作、字符串运算接口、常量成员和非成员函数等内容。通过实例演示了如何使用这些接口进行字符串的创建、修改、查找和比较等操作,帮助读者更好地理解和掌握`string`类的应用。
77 2
|
2月前
|
存储 编译器 C++
【c++】类和对象(下)(取地址运算符重载、深究构造函数、类型转换、static修饰成员、友元、内部类、匿名对象)
本文介绍了C++中类和对象的高级特性,包括取地址运算符重载、构造函数的初始化列表、类型转换、static修饰成员、友元、内部类及匿名对象等内容。文章详细解释了每个概念的使用方法和注意事项,帮助读者深入了解C++面向对象编程的核心机制。
128 5
|
2月前
|
存储 编译器 C++
【c++】类和对象(中)(构造函数、析构函数、拷贝构造、赋值重载)
本文深入探讨了C++类的默认成员函数,包括构造函数、析构函数、拷贝构造函数和赋值重载。构造函数用于对象的初始化,析构函数用于对象销毁时的资源清理,拷贝构造函数用于对象的拷贝,赋值重载用于已存在对象的赋值。文章详细介绍了每个函数的特点、使用方法及注意事项,并提供了代码示例。这些默认成员函数确保了资源的正确管理和对象状态的维护。
138 4
|
2月前
|
存储 编译器 Linux
【c++】类和对象(上)(类的定义格式、访问限定符、类域、类的实例化、对象的内存大小、this指针)
本文介绍了C++中的类和对象,包括类的概念、定义格式、访问限定符、类域、对象的创建及内存大小、以及this指针。通过示例代码详细解释了类的定义、成员函数和成员变量的作用,以及如何使用访问限定符控制成员的访问权限。此外,还讨论了对象的内存分配规则和this指针的使用场景,帮助读者深入理解面向对象编程的核心概念。
195 4