item32 确定你的public继承塑模出is-a关系

:::info is-a关系是什么?
可以理解成一种类似集合的子集的概念——基类包含于派生类中
所有对基类可以进行的操作都可以施加在派生类上,否则说明这种继承关系是错误的。 :::

  1. class Bird{
  2. void fly();
  3. }
  4. class Penguin:public Bird{//企鹅是一种鸟
  5. //但是企鹅不会飞,这种继承关系有误
  6. }

class之间的关系: :::tips

  • is-a
  • has-a
  • is-implemented-in-terms-of(根据某物出现) :::

    item33 避免遮掩继承而来的名称

    内层作用域名称会遮掩外围作用域的名称
    实际上对于基类和派生类也存在这种问题:
  1. 派生类的作用域被嵌套在基类中
  2. 派生类如果重载了基类的函数,那么基类的函数在派生类中会被遮蔽掉

避免方式:
用using声明式
using Base::mf1
使用转交函数

  1. class Base{
  2. public:
  3. virtual void mf1()=0;
  4. virtual void mf1(int);
  5. };
  6. class Derived:private Base{
  7. public:
  8. virtual void mf1(){
  9. Base::mf1();
  10. }
  11. }

:::tips

  • derived classes内名称会遮掩base classes内的名称。
  • 可以用using声明和转交函数来避免。 :::

    item34 区分接口继承和实现继承

  • 成员函数的接口总会被继承

  • 春虚函数的目的是为了让派生类只继承函数接口
  • 非纯虚函数的目的是让派生类能继承接口和缺省实现

但是利用非纯虚函数时,客户可能会忘记声明和定义,这样会调用缺省的实现
避免这种问题的方式:

  • 切断virtual函数和缺省实现之间的连接

    1. class Airplane{
    2. public:
    3. virtual void fly(const Airport& destination)=0;
    4. protected:
    5. void defaultFly(const Airport& destination){
    6. fly to someplace;
    7. }
    8. }

    将函数声明为纯虚函数,强迫客户实现,同时提供一个缺省的实现

    1. class ModelA:public Airplane{
    2. public:
    3. virtual void fly(const Airport& destination){
    4. defaultFly(destination);
    5. }
    6. }

    在建立派生类时,可以通过主动调用函数来完成缺省实现的继承。

    小技巧

  • 另一种方案是根据纯虚函数必须在派生类中重新声明,但是他们也可以拥有自己的实现

    1. class Airplane{
    2. public:
    3. virtual void fly(const Airport& destination)=0;
    4. }
    5. void Airplane::fly(const Airport& destination){
    6. //..
    7. }
    8. class ModelA:public Airplane{
    9. public:
    10. virtual void fly(const Airport& destination){
    11. Airplane::fly(destination);//
    12. }
    13. }

    :::info 可以使用纯虚函数的定义来替代上版本的protected函数
    本质上讲fly被分割成两个部分:

  1. 声明部分表示的是接口,这是派生类必须继承的
  2. 定义部分表示的是缺省实现,只有当明确提出申请时才会使用:Airplane::fly(destination)

不能将这两个部分合并,因为这样就丧失了让两个函数享有不同的保护级别的机制 :::

:::tips

  • 派生类总会继承基类的接口
  • 纯虚函数只指定接口继承
  • 非纯虚函数指定具体的接口继承和缺省实现继承
  • 非虚函数实现和接口都继承 :::

    item35 考虑virtual函数以外的其他选择