15-2 定义基类和派生类

15.2.1 定义基类

类的定义

定义Quote类

class Quote{
public : 
    Quote() =  default;
    Quote(const string &book, double sales_price):
    	bookNo(book), price(sales_price) {}
    string isbn() const {return bookNo;}
    //返回给定数量的书籍的销售总额
    //派生类负责编写并使用不同的折扣算法
    virtual double net_price(int n) const{
        return n*price;
    }
    virtual ~Quote() = default;  //对析构函数进行动态绑定
private :
    string bookNo;      //书籍的ISBN号
protected : 
    double price = 0.0; //代表普通状态下不打折的价格
}

基类通常都应该定义一个虚析构函数,即使该函数不执行任何实际操作也是如此。

成员函数与继承

从基类的角度说

  1. 类型无关的成员,直接定义
  2. 类型相关的成员,定义为虚函数

从派生类角度说

  1. 派生类会继承基类的成员
  2. 可以覆盖基类的虚函数

从使用者角度说

在用基类的指针或引用调用函数时会发生动态绑定

关于虚函数virtual

  1. 可以把构造函数之外的任何非静态函数声明为虚函数
  2. virtual只能出现在类内部的声明语句中
  3. 派生类如果没有覆盖虚函数,则会直接继承基类的版本

更多关于虚函数的讨论详见15.3

访问控制与继承

访问说明符 对类成员 对类用户 对派生类
public 可见 可见 可见
private 可见 不可见 不可见
protected 可见 不可见 可见

15.2.2 定义派生类

类的定义

class Bulk_quote : public Quote{ //Bulk_quote继承自Quote
public :
    Bulk_quote() = default;
    Bulk_quote(const stirng&, double, int, double);
    //覆盖基类的函数版本来实现大量购买的折扣政策
    double net_price(int ) const override;
private :
    int min_qty = 0; 		//使用折扣政策的最低购买量
    double discount; = 0.0; //以小数表示的折扣额
}
  1. Bulk_quote是Quote的派生类
  2. Bulk_quote继承了Quote中的成员如isbn\bookNo\price等
  3. Bulk_quote重定义(覆盖)虚函数net_price
  4. Bulk_quote新增了两个数据成员

派生类向基类的类型转换

一个派生类对象包含多个组成部分:

  1. 一个含有派生类自己定义的(非静态)成员的子对象

  2. 一个与该派生类继承的基类对应的子对象

    如果有多个基类,那么这样的子对象也有多个

image-20220302162944781

因为在派生类对象中含有与其基类对应的组成部分

  • 能把派生类的对象当成基类对象来使用
  • 能将基类的指针或引用绑定到派生类对象中的基类部分上。
Quote item;			//基类对象
Bulk_quote bulk;	//派生类对象
Quote *p = &item;	//p指向基类对象
p = &bulk;			//p指向bulk的Quote部分
Quote &r = bulk;	//r绑定到bulk的Quote部分

这种转换称之为派生类向基类的类型转换

在派生类对象中含有与其积累对应的组成部分,这是继承的关键所在。

image-20220302163848028

派生类构造函数

每个类控制自己的成员初始化过程(接口与类耦合)

派生类无法直接初始化从基类继承的成员,必须使用基类的构造函数来初始化它的基类部分

//实现Bulk_quote的构造函数
Bulk_quote(const string &book, double p,
           int qty, double disc) : 
			Quote(book,p), min_qty(qty), discount(disc){}

派生类的初始化过程:

  1. 初始化基类部分:委托基类的构造函数

    没有显式调用基类的构造函数则基类部分进行默认初始化,此时如果基类没有默认构造函数就会报错

  2. 初始化派生类自己成员

如果继承有好几个层级:派生类只初始化它的直接基类,详见15.4

继承与静态成员

如果基类定义了一个静态成员

  1. 整个继承体系中只会由static成员的唯一定义和唯一实例
  2. static成员遵循通用的访问控制规则

image-20220302165451857

image-20220302165458027

防止继承的发生

final防止继承

class Z final {/**/};  	   //Z不能作为基类
class A : public Z {/**/}; //错误:Z是final的

15.2.3 类型转换与继承

静态类型与动态类型

  • 静态类型(static type):编译时已知
  • 动态类型(dynamic type):运行时才可知

当基类的指针或引用调用虚函数时,会发生动态绑定,此时基类对象的静态类型和动态类型不一致【引用对象的静态类型是基类,动态类型可能是派生类】。

其他情况下,静态类型与动态类型一定一致

派生类给基类赋值

当我们用一个派生类对象为一个基类对象初始化或赋值时,只有该派生类对象中的基类部分会被拷贝、移动或赋值,它的派生类部分将被忽略掉。

image-20220302170902630

总结

image-20220302171046313

posted @ 2022-03-04 12:43  咪啪魔女  阅读(220)  评论(0编辑  收藏  举报