刘胥影 liuxy@seu.edu.cn 东南大学计算机学院 面向对象程序设计1 2010~2011第3学期 刘胥影 liuxy@seu.edu.cn 东南大学计算机学院
Inheritance 教学要求与安排 教学要求 全部要求 东南大学计算机学院 11/24/2018
Inheritance 12.1 继承简介 12.2 定义基类和派生类 12.3 实例研究 12.4 派生类中的构造和析构函数 (chap12.1, 12.2) (chap12.3,12.6,12.7) (chap12.4) (chap 12.5)
继承简介 继承 继承的分类 继承关系的树状结构 定义继承关系
继承(1) 继承(Inheritance) 派生类可以做什么? 利用现有类的数据和行为来构建新类,并增加新的特性增强 此类 继承基类成员 12.1继承简介 继承(1) 继承(Inheritance) 利用现有类的数据和行为来构建新类,并增加新的特性增强 此类 派生类可以做什么? 继承基类成员 增加成员 重定义与自身特性相关的成员函数(虚函数) 基类 派生类 继承 关注共性 关注特性 交通工具 汽车 继承 火车 e.g. 是不是只有public继承才有isa关系 东南大学计算机学院 11/24/2018
继承(2) book():返回书的ISBN net_price():返回购买指定数量的书的价格 书 Item_base 打折书 12.1继承简介 继承(2) 书 Item_base 打折书 Bulk_item 继承 book():返回书的ISBN 在派生类中不需要改变 net_price():返回购买指定数量的书的价格 需要在派生类中改变 在基类中声明为virtual函数 是不是只有public继承才有isa关系 东南大学计算机学院 11/24/2018
继承(3) 继承 组成(Chap10.3) 基类 派生类 A类 int x; B b; B类 软件复用性可以提高软件开发的质量和效率 12.1继承简介 继承(3) 继承 组成(Chap10.3) 基类 派生类 继承 e.g. 汽车 is-a 交通工具 派生类对象 基类对象 is-a关系 (public继承) A类 int x; B b; B类 数据成员为其他类对象 e.g. 汽车 has-a 发动机 成员对象 宿主对象 has-a关系 软件复用性可以提高软件开发的质量和效率 东南大学计算机学院 11/24/2018
继承的分类(1) 按继承的级数分类 单级继承 多级继承 基类 派生类 基类 派生类1 派生类2 12.1继承简介 继承 直接基类 直接基类 间接基类 东南大学计算机学院 11/24/2018
继承的分类(2) 按基类的个数分类 单继承 多继承 基类 派生类 基类1 派生类 基类2 12.1继承简介 继承 继承 东南大学计算机学院 11/24/2018
继承的分类(3) 按对基类的访问权限分类 public继承:基类成员的访问权限不变 protected继承 private继承 12.1继承简介 继承的分类(3) 按对基类的访问权限分类 public继承:基类成员的访问权限不变 protected继承 private继承 public成员 protected成员 private成员 基类 public继承 派生类 protected继承 private继承 东南大学计算机学院 11/24/2018
12.1继承简介 继承关系的树状结构 根类 东南大学计算机学院 11/24/2018
定义基类和派生类 基类的定义 protected成员 定义派生类 访问控制 文件组织 东南大学计算机学院 11/24/2018
基类的定义 书 Item_base 打折书 Bulk_item 继承 12.2继承简介 class Item_base{ public: Item_base(const string &book = “”, double sales_price = 0.0): isbn(book),price(sales_price){ } string book() const {return book} virtual double net_price(int n) const { return n * price; } virtual ~Item_base(){ } private: string isbn; protected: double price; }; 书 Item_base 打折书 Bulk_item 继承 东南大学计算机学院 11/24/2018
protected成员(1) public成员 private成员 可被本类的其他成员访问 可被本类的友元访问 可通过本类对象的句柄访问 12.2继承简介 protected成员(1) public成员 可被本类的其他成员访问 可被本类的友元访问 可通过本类对象的句柄访问 句柄:对象、引用、指针 private成员 类外不可访问 东南大学计算机学院 11/24/2018
protected成员(2) protected成员 可被本类的其他成员访问 可被本类的友元访问 可被本类的任何派生类的成员和友元访问 12.2继承简介 protected成员(2) protected成员 可被本类的其他成员访问 可被本类的友元访问 可被本类的任何派生类的成员和友元访问 需要的访问权限 成员属性 对数据的保护强度 private > protected > public 东南大学计算机学院 11/24/2018
protected成员(3) 面向类用户的接口:public成员 面向派生类用户的“接口”:public成员和protected成员 12.2继承简介 protected成员(3) 可访问性 基类成员 本类成员 本类友元 类用户 派生类 Public成员 Protected成员 Private成员 private:宁与友(友元),不与子(派生类) 面向类用户的接口:public成员 面向派生类用户的“接口”:public成员和protected成员 东南大学计算机学院 11/24/2018
protected成员(4) 设计基类的一般性原则 接口:public 禁止派生类访问的成员:private 12.2继承简介 protected成员(4) 设计基类的一般性原则 接口:public 禁止派生类访问的成员:private 提供给派生类使用的成员:protected 面向类用户的接口:public成员 面向派生类用户的“接口”:public成员和protected成员 东南大学计算机学院 11/24/2018
protected成员(4) 派生类访问protected成员 只能通过派生类对象访问基类的protected成员 12.2继承简介 protected成员(4) 派生类访问protected成员 只能通过派生类对象访问基类的protected成员 对基类对象的protected成员无访问权限 书 Item_base 打折书 Bulk_item 继承 void Bulk_item::f(Bulk_item &d, Item_base &b){ double v = price; //ok v = d.price; //ok,通过派生类对象访问 v = b.price; //error,通过基类对象访问 } protected: price 东南大学计算机学院 11/24/2018
class classname : access-label base-class 定义派生类(1) 类派生列表(class derivation list) class classname : access-label base-class public/protected/private继承 基类名 派生类名 class Bulk_item : public Item_base{ public: double net_price(int n) const; //redefines base version private: int min_qty; //minimum purchase for discount to apply double discount; // fractional discount to apply } 东南大学计算机学院 11/24/2018
定义派生类(2) 派生类对象的组成 基类的成员 自己定义的成员 Item_base对象 -string isbn: private -double price: protected Bulk_item对象 -string isbn: private -double price: protected -int min_qty: private -double discount: private Item_base成员 Bulk_item成员 东南大学计算机学院 11/24/2018
定义派生类(3) 派生类可以访问基类的public和protected成员 Bulk_item对象 -string isbn: private -double price: protected -int min_qty: private -double discount: private item_base成员 Bulk_item成员 double Bulk_item::net_price(int cnt) const{ if( cnt >= min_qty ) return cnt * (1-discount) * price; else return cnt * price; } 东南大学计算机学院 11/24/2018
定义派生类(4) 一些规定 基类必须是已定义的类 虚函数的声明需和基类中的声明一致 派生类可作为基类(多级继承) 仅仅声明是不够的 class Item_base; //类声明 class Bulk_item : public Item_base{……}; //error Base D1 继承 D2 class Base{……}; class D1 : public Base{……}; class D2 : public D1{……}; 东南大学计算机学院 11/24/2018
访问控制(1) 区分两类访问属性 12.2继承简介 类用户 类用户对类成员的访问属性 基类 派生类 派生类对从基类继承的成员的访问属性 基类用户 派生类用户 东南大学计算机学院 11/24/2018
派生类可访问基类的public成员和protected成员 12.2继承简介 访问控制(2) 派生类对从基类继承的成员的访问属性 可访问性 基类成员 本类成员 本类友元 类用户 派生类 Public成员 Protected成员 Private成员 派生类可访问基类的public成员和protected成员 和继承方式无关 东南大学计算机学院 11/24/2018
派生类可访问基类的public成员和protected成员 12.2继承简介 访问控制(3) 派生类对从基类继承的成员的访问属性 class Base{ public: void basef(); protected: int x; }; class Public_D : public Base{ int f( ){ return x; } //ok }; class Private_D : private Base{ int f( ){ return x; } //ok 派生类可访问基类的public成员和protected成员 和继承方式无关 东南大学计算机学院 11/24/2018
访问控制(4) 派生类的成员 类用户对派生类自己定义的成员的访问 从基类中继承来的基类的成员 自己定义的成员 由该派生类自己控制 12.2继承简介 访问控制(4) 派生类的成员 从基类中继承来的基类的成员 public和protected成员可见 private成员被隐藏 自己定义的成员 类用户对派生类自己定义的成员的访问 由该派生类自己控制 每个类控制它所定义的成员的访问属性 东南大学计算机学院 11/24/2018
访问控制(5) 类用户对继承成员的访问属性取决于 基类中的该成员的访问属性 派生列表中使用的访问属性(继承属性) 12.2继承简介 基类定义了成员的最小访问控制 public成员 protected成员 private成员 基类 public继承 派生类 protected继承 private继承 派生类可进一步限制但不能放松对继承成员的访问 东南大学计算机学院 11/24/2018
访问控制(6) 12.2继承简介 class Base{ public: void basef(); protected: int x; }; Base b; Public_D d1; Private_D d2; b.basef(); //ok d1.basef(); //ok, public d2.basef(); //error, private class Public_D : public Base{ int f( ){ return x; } //ok }; class Private_D : private Base{ int f( ){ return x; } //ok 东南大学计算机学院 11/24/2018
内容回顾 继承定义 访问控制 利用现有类的数据和行为来构建新类,并增加新的特性 增强此类 单级/多级继承 单/多继承 public/protected/private继承 访问控制 两类访问控制 类用户对类 派生类对基类 protected成员:派生类可以访问 public/protected/private继承时的类用户访问 东南大学计算机学院 11/24/2018
内容回顾 public/protected/private继承时的类用户访问 public成员 protected成员 private成员 基类 public继承 派生类 protected继承 private继承 东南大学计算机学院 11/24/2018
文件组织 基类实现 基类接口 类用户代码 派生类实现 派生类接口 定义派生类时需包含基类的头文件 12.2继承简介 文件组织 派生类用户 派生类类设计者 基类设计者 A.cpp Main.obj EXE 基类实现 基类接口 类用户代码 A.h A.obj B.cpp 派生类实现 派生类接口 B.h B.obj Main.cpp 定义派生类时需包含基类的头文件 基类设计者只需向派生类设计者提供基类的头文件和目标文件,而无需提供基类的实现 基类设计的良好原则:基类的实现改变时不影响派生类 东南大学计算机学院 11/24/2018
实例研究 以非继承方式实现 以继承方式实现 东南大学计算机学院 11/24/2018
实例研究 非继承方式实现 VS. 继承方式实现 佣金雇员 CommissionEmployee 带底薪佣金雇员 12.3实例研究 实例研究 佣金雇员 CommissionEmployee 带底薪佣金雇员 BaseCommissionEmployee earnings: grossSales × commissionRate baseSalary + grossSales × commissionRate CommissionEmployee -firstName -lastName -socialSecurityNumber -grossSales -commissionRate BaseCommissionEmployee -firstName -lastName -socialSecurityNumber -grossSales -commissionRate -baseSalary 非继承方式实现 VS. 继承方式实现 东南大学计算机学院 11/24/2018
以非继承方式实现(1) CommissionEmployee类 BaseCommissionEmployee类 类定义(Fig. 12.4) 12.3实例研究 以非继承方式实现(1) CommissionEmployee类 类定义(Fig. 12.4) 类实现(Fig. 12.5) BaseCommissionEmployee类 类定义(Fig. 12.7) 类实现(Fig. 12.8) 东南大学计算机学院 11/24/2018
BaseCommissionEmployee 12.3实例研究 以非继承方式实现(2) 两个类的异同 CommissionEmployee -firstName -lastName -socialSecurityNumber -grossSales -commissionRate BaseCommissionEmployee -firstName -lastName -socialSecurityNumber -grossSales -commissionRate -baseSalary 东南大学计算机学院 11/24/2018
BaseCommissionEmployee 12.3实例研究 以非继承方式实现(3) CommissionEmployee -setFirstName() -getFirstName() -setLastName() -getLastName() -setSocialSecurityNumber() -getSocialSecurityNumber() -setGrossSales() -getGrossSales() -setCommissionRate() -getCommissionRate() -earnings() -print() BaseCommissionEmployee -setFirstName() -getFirstName() -setLastName() -getLastName() -setSocialSecurityNumber() -getSocialSecurityNumber() -setGrossSales() -getGrossSales() -setCommissionRate() -getCommissionRate() -setBaseSalary() -getBaseSalary() -earnings() -print() 东南大学计算机学院 11/24/2018
12.3实例研究 以非继承方式实现(4) 缺点 代码大量重复 效率低 不利于维护 东南大学计算机学院 11/24/2018
BaseCommissionEmployee 12.3实例研究 以继承方式实现(2) 基类 派生类 继承 关注共性 关注特性 CommissionEmployee -firstName -lastName -socialSecurityNumber -grossSales -commissionRate BaseCommissionEmployee -firstName -lastName -socialSecurityNumber -grossSales -commissionRate -baseSalary 共性 特性:需增加 东南大学计算机学院 11/24/2018
BaseCommissionEmployee 12.3实例研究 以继承方式实现(2) CommissionEmployee -setFirstName() -getFirstName() -setLastName() -getLastName() -setSocialSecurityNumber() -getSocialSecurityNumber() -setGrossSales() -getGrossSales() -setCommissionRate() -getCommissionRate() -earnings() -print() BaseCommissionEmployee -setFirstName() -getFirstName() -setLastName() -getLastName() -setSocialSecurityNumber() -getSocialSecurityNumber() -setGrossSales() -getGrossSales() -setCommissionRate() -getCommissionRate() -setBaseSalary() -getBaseSalary() -earnings() -print() 共 性 特性:需增加 特性:需重新定义
以继承方式实现(3) 12.3实例研究 Fig. 12.10: BasePlusCommissionEmployee.h 东南大学计算机学院 11/24/2018
以继承方式实现(4) 成员初始化表进行初始化操作,而非赋值操作。而初始化仅能仅能一次,因此使用成员初始化表保证数据仅被初始化一次 12.3实例研究 以继承方式实现(4) Fig. 12.11: BasePlusCommissionEmployee.cpp 成员初始化表进行初始化操作,而非赋值操作。而初始化仅能仅能一次,因此使用成员初始化表保证数据仅被初始化一次 12.4 派生类的构造和析构函数 东南大学计算机学院 11/24/2018
以继承方式实现(5) 12.3实例研究 Fig. 12.11: BasePlusCommissionEmployee.cpp 东南大学计算机学院 11/24/2018
以继承方式实现(6) 12.3实例研究 不能访问基类的private成员! Fig. 12.11: BasePlusCommissionEmployee.cpp 东南大学计算机学院 11/24/2018
以继承方式实现(7) 派生类访问基类的private成员的两种解决方案 方案1:将private成员声明为protected成员 12.3实例研究 以继承方式实现(7) 派生类访问基类的private成员的两种解决方案 方案1:将private成员声明为protected成员 方案2:保持private属性不变,使用set和get等成员函数 东南大学计算机学院 11/24/2018
以继承方式实现(8) 方案1:将private成员声明为protected成员 12.3实例研究 基类声明的其他部分和类实现不变 Fig. 12.12: CommissionEmployee.h 基类声明的其他部分和类实现不变 东南大学计算机学院 11/24/2018
以继承方式实现(9) 12.3实例研究 OK! 派生类声明和类实现均不变 Fig. 12.15: BasePlusCommissionEmployee.cpp 派生类声明和类实现均不变 东南大学计算机学院 11/24/2018
以继承方式实现(10) 方案2:保持private属性不变,使用set和get等成员函数 12.3实例研究 基类声明的其他部分不变 Fig. 12.17: CommissionEmployee.h(基类定义) 基类声明的其他部分不变 东南大学计算机学院 11/24/2018
Fig. 12.18: CommissionEmployee.cpp (基类实现) 尽量使用成员初始化列表进行初始化 尽量使用set和get函数 Fig. 12.18: CommissionEmployee.cpp (基类实现) 11/24/2018 东南大学计算机学院
以继承方式实现(12) 12.3实例研究 基类定义的其他部分不变 尽量使用set和get函数 Fig. 12.18: CommissionEmployee.cpp (基类实现) 基类定义的其他部分不变 11/24/2018 东南大学计算机学院
以继承方式实现(13) 12.3实例研究 派生类声明不变 派生类实现:使用set和get等成员函数访问基类的private成员 ( commissionRate * grossSales ) 使用成员函数访问基类的private成员 Fig. 12.20: BasePlusCommissionEmployee.cpp (派生类实现) 东南大学计算机学院
派生类中的构造和析构函数 派生类对基类成员函数的继承 派生类中构造函数的调用 派生类中析构函数的调用 例子 东南大学计算机学院 11/24/2018
派生类对基类成员函数的继承 对于基类的 构造函数 析构函数 重载的赋值运算符 派生类不会继承它们,但是可以调用它们 12.4派生类中的构造和析构函数 派生类对基类成员函数的继承 对于基类的 构造函数 析构函数 重载的赋值运算符 派生类不会继承它们,但是可以调用它们 东南大学计算机学院 11/24/2018
在创建对象时,为对象分配内存和进行初始化 12.4派生类中的构造和析构函数 派生类中构造函数的调用(1) 构造函数的作用: 在创建对象时,为对象分配内存和进行初始化 析构函数的作用: (准备) 释放对象占用的内存 派生类对象的组成 基类的数据成员 自定义的数据成员 基类定义 基类的构造和析构函数 派生类定义 派生类的构造和析构函数 东南大学计算机学院 11/24/2018
派生类中构造函数的调用(2) 构造函数的调用顺序 12.4派生类中的构造和析构函数 函数调用顺序 派生类B 派生类B的构造函数 函数执行顺序 基类A的构造函数 B b; 函数调用顺序 函数执行顺序 派生类B 基类A 东南大学计算机学院 11/24/2018
派生类中构造函数的调用(2) 构造函数的调用顺序 多级继承 12.4派生类中的构造和析构函数 派生类C的构造函数 派生类C 函数调用顺序 派生类B的构造函数 基类A的构造函数 C c; 函数调用顺序 函数执行顺序 派生类C的构造函数 派生类B 基类A 派生类C 东南大学计算机学院 11/24/2018
派生类中构造函数的调用(2) 构造函数的调用顺序 带有成员初始化表的构造函数 12.4派生类中的构造和析构函数 函数调用顺序 基类A的构造函数 B b; 函数调用顺序 函数执行顺序 初始化表 函数体 派生类B的构造函数 派生类B 基类A 东南大学计算机学院 11/24/2018
派生类中构造函数的调用(2) 构造函数的调用方式 隐式调用:调用默认的构造函数 12.4派生类中的构造和析构函数 派生类中构造函数的调用(2) 构造函数的调用方式 隐式调用:调用默认的构造函数 显示调用: 在派生类的构造函数中,一般在成员初始化表中调用构 造函数,避免多次修改数据成员 东南大学计算机学院 11/24/2018
派生类中析构函数的调用 析构函数的调用顺序与构造函数相反 12.4派生类中的构造和析构函数 函数执行顺序 派生类C的析构函数 派生类C 派生类B的析构函数 基类A的析构函数 {C c;} 函数调用顺序 函数执行顺序 派生类C的析构函数 派生类B 基类A 派生类C 东南大学计算机学院 11/24/2018
例子 Fig. 12.26: fig12_26.cpp CommissionEmployee constructor CommissionEmployee destructor CommissionEmployee constructor: BasePlusCommissionEmployee constructor: CommissionEmployee constructor: BasePlusCommissionEmployee constructor: BasePlusCommissionEmployee destructor: CommissionEmployee destructor:
Inheritance 教学要求与安排 教学要求 全部要求 东南大学计算机学院 11/24/2018
课堂练习 Ex. 12.6 Ex. 12.8 Quadrilateral(四边形) Trapezoid(梯形) Parallelogram(平行四边形) Rectangle(矩形) Square(正方形) 东南大学计算机学院 11/24/2018
Ans to Ex.12.6 东南大学计算机学院 11/24/2018
Ans to Ex.12.8 东南大学计算机学院 11/24/2018
作业 deadline: 5.12 24:00 H6: Ex12.9, 12.10 东南大学计算机学院 11/24/2018