5 多态

5.1 非虚的世界

5.2 虚函数与多态

5.2.1 通过指向子类对象的基类指针调用虚函数

基于虚函数的运行时多态:

5.2.2 虚函数覆盖的条件

对基类中的虚函数,子类可以用一个具有相同签名(即函数名相同,参数表相同,常限定相同)的成员函数进行覆盖。为使覆盖有效需要满足以下条件:

  1. 如果基类中的虚函数返回一个基本类型的数据,那么该函数在子类中的覆盖版本也必须返回相同类型的数据

  2. 如果基类中的虚函数返回一个类类型的指针或引用,那么该函数在子类中的覆盖版本可以返回其子类类型的指针或引用——类型协变

  3. 无论基类中的虚函数位于公有、保护还是私有部分,该函数在子类中的覆盖版本都可以出现在包括公有、保护及私有在内的任何部分

5.2.3 多态性的条件

5.2.3.1 多态性必须借助指针或引用才能表现出来

5.2.3.2 子类调基类靠继承,基类调子类靠多态

5.2.4 操作符函数的多态性

5.2.5 纯虚函数、抽象类和纯抽象类

5.2.6 动态绑定

5.2.6.1 动态绑定的实现机理——虚函数表

当编译器看到下面这条语句时:

它并不知道pa所指向对象的真实身份,编译器所能做的就是用一段代码替代上面这行调用语句。这段代码将依次执行下列操作:

  1. 首先弄清指针pa所指向对象的真实身份

  2. 然后通过这个对象的虚函数表指针_vftbl访问其虚函数表,并找到与foo标识符相对应的虚函数代码入口地址

  3. 根据入口地址,调用该函数

5.2.6.2 动态绑定对性能的影响

5.2.7 好莱坞模式——职责分离

5.3 运行时类型信息——RTTI

5.3.1 动态类型转换

5.3.2 typeid操作符

5.4 虚析构函数

5.4.1 通过基类指针析构子类对象的问题

当delete操作符作用于一个指向子类对象的基类指针时,如果基类的析构函数没有被声明为虚函数,那么实际被调用的将仅仅是基类的析构函数,而基类的析构函数并不会自动调用子类的析构函数,因此实际被销毁的将仅仅是这个子类对象中基类子对象所创建的动态资源,而该子类对象自己的动态资源将失去被释放的机会,形成泄漏

5.4.2 虚析构方案

当delete操作符作用于一个指向子类对象的基类指针时,如果基类的析构函数被声明为虚函数,那么实际被调用的将是子类的析构函数,而子类的析构函数在释放完子类对象自己的动态资源后,会自动调用基类的析构函数,完成对子类对象中基类子对象所创建动态资源的释放,避免泄漏

5.4.3 空虚析构函数

思考:构造函数可以是虚函数吗?静态成员函数可以是虚函数吗?