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


目录
相关文章
|
1月前
|
编译器 C++ 开发者
【C++】继承
C++中的继承是面向对象编程的核心特性之一,允许派生类继承基类的属性和方法,实现代码复用和类的层次结构。继承有三种类型:公有、私有和受保护继承,每种类型决定了派生类如何访问基类成员。此外,继承还涉及构造函数、析构函数、拷贝构造函数和赋值运算符的调用规则,以及解决多继承带来的二义性和数据冗余问题的虚拟继承。在设计类时,应谨慎选择继承和组合,以降低耦合度并提高代码的可维护性。
33 1
【C++】继承
|
1月前
|
存储 编译器 C语言
【c++丨STL】string类的使用
本文介绍了C++中`string`类的基本概念及其主要接口。`string`类在C++标准库中扮演着重要角色,它提供了比C语言中字符串处理函数更丰富、安全和便捷的功能。文章详细讲解了`string`类的构造函数、赋值运算符、容量管理接口、元素访问及遍历方法、字符串修改操作、字符串运算接口、常量成员和非成员函数等内容。通过实例演示了如何使用这些接口进行字符串的创建、修改、查找和比较等操作,帮助读者更好地理解和掌握`string`类的应用。
51 2
|
1月前
|
存储 编译器 C++
【c++】类和对象(下)(取地址运算符重载、深究构造函数、类型转换、static修饰成员、友元、内部类、匿名对象)
本文介绍了C++中类和对象的高级特性,包括取地址运算符重载、构造函数的初始化列表、类型转换、static修饰成员、友元、内部类及匿名对象等内容。文章详细解释了每个概念的使用方法和注意事项,帮助读者深入了解C++面向对象编程的核心机制。
106 5
|
1月前
|
存储 编译器 C++
【c++】类和对象(中)(构造函数、析构函数、拷贝构造、赋值重载)
本文深入探讨了C++类的默认成员函数,包括构造函数、析构函数、拷贝构造函数和赋值重载。构造函数用于对象的初始化,析构函数用于对象销毁时的资源清理,拷贝构造函数用于对象的拷贝,赋值重载用于已存在对象的赋值。文章详细介绍了每个函数的特点、使用方法及注意事项,并提供了代码示例。这些默认成员函数确保了资源的正确管理和对象状态的维护。
97 4
|
1月前
|
存储 编译器 Linux
【c++】类和对象(上)(类的定义格式、访问限定符、类域、类的实例化、对象的内存大小、this指针)
本文介绍了C++中的类和对象,包括类的概念、定义格式、访问限定符、类域、对象的创建及内存大小、以及this指针。通过示例代码详细解释了类的定义、成员函数和成员变量的作用,以及如何使用访问限定符控制成员的访问权限。此外,还讨论了对象的内存分配规则和this指针的使用场景,帮助读者深入理解面向对象编程的核心概念。
116 4
|
2月前
|
存储 编译器 对象存储
【C++打怪之路Lv5】-- 类和对象(下)
【C++打怪之路Lv5】-- 类和对象(下)
32 4
|
2月前
|
存储 安全 C++
【C++打怪之路Lv8】-- string类
【C++打怪之路Lv8】-- string类
29 1
|
2月前
|
编译器 C语言 C++
【C++打怪之路Lv4】-- 类和对象(中)
【C++打怪之路Lv4】-- 类和对象(中)
33 4
|
2月前
|
存储 编译器 C++
【C++类和对象(下)】——我与C++的不解之缘(五)
【C++类和对象(下)】——我与C++的不解之缘(五)
|
2月前
|
编译器 C++
【C++类和对象(中)】—— 我与C++的不解之缘(四)
【C++类和对象(中)】—— 我与C++的不解之缘(四)