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 @   咪啪魔女  阅读(282)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· Ollama——大语言模型本地部署的极速利器
· 使用C#创建一个MCP客户端
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· Windows编程----内核对象竟然如此简单?
· ollama系列1:轻松3步本地部署deepseek,普通电脑可用
more_horiz
keyboard_arrow_up light_mode palette
选择主题
menu
点击右上角即可分享
微信分享提示