13-虚函数

虚函数

  • 定义: 在实现C++多态时会用到虚函数,虚函数使用的其核心目的是__通过基类访问派生类定义的函数__ 。所谓虚函数就是在基类定义一个未实现的函数名,为了提高程序的可读性,建议后代虚函数都加上virtual关键字
  • 常见用法:声明基类指针,利用指针指向任意一个子类对象,调用相关的虚函数,动态绑定,由于编写代码时不能确定被调用的是基类函数还是那个派生类函数,所以被称为“”虚“”函数。如果没有使用虚函数的话,即没有利用C++多态性,则利用基类指针调用相应的函数的时候,将总被限制在基类函数本身,而无法调用到子类中被重写过的函数。

虚指针(vptr)和虚表(vtbl)

  • 在类的继承中,每一个class产生一堆指向virtual funciton的指针,这些指针统一放在vtbl(虚表)中。对于每一个class都被添加了一个指针,指向相关的vtbl(虚表),称为vptr(虚指针)。 ![[../../../../Cache/images/23-虚函数/image-113011.png]]

纯虚函数

定义:

  • 纯虚函数是在基类中声明的虚函数,它在基类中没有定义,但要求任何派生类都要定义自己的实现方法。
  • 在基类中实现纯虚函数的方法是在函数原型后加=0
    virtual void function() = 0;

引入原因:

在很多情况下,基类本身生成对象是不合情理的。例如,动物作为一个基类可以派生出老虎、孔雀等子类,但动物本身生成对象明显不合常理。为了解决上述问题,引入了纯虚函数的概念,将函数定义为纯虚函数(方法:virtual ReturnType Function()= 0;),则编译器要求在派生类中必须予以重写以实现多态性。同时含有纯虚拟函数的类称为抽象类,它不能生成对象。这样就很好地解决了上述两个问题

  • 纯虚函数最显著的特征是:他们必须在继承类中重新声明函数,否则该派生类也无法实例化。

抽象类:

  • 包含纯虚函数的类称为抽象类
  • 抽象类是一中特殊的类,它是为了抽象和设计的目的建立的,它处于继承层次结构的较上层
  • 抽象类的作用:
    • 抽象类的主要作用是将有关的操作作为结果接口组织在一个继承层次结构中,由它来为派生类提供一个公共的根,派生类将具体实现在其基类中作为接口的操作。所以派生类实际上刻画了一组子类的操作接口的通用语义,这些语义也传给子类,子类可以具体实现这些语义,也可以再将这些语义传给自己的子类。

使用时需注意

  • 抽象类只能作为基类来使用,其纯虚函数的实现由派生类给出。如果派生类中没有重新定义纯虚函数,而只是继承基类的纯虚函数,则这个派生类仍然还是一个抽象类。如果派生类中给出了基类纯虚函数的实现,则该派生类就不再是抽象类了,它是一个可以建立对象的具体的类。
  • 抽象类是不能定义对象的。

4. 虚函数与纯虚函数

  • 首先,定义一个函数为虚函数,不代表函数为不被实现的函数。
    定义它为虚函数是__为了允许基类的指针来调用子类的这个函数__。

  • 定义一个函数为__纯虚函数,才代表函数没有被实现__
    定义纯虚函数是为了实现一个接口,起到一个规范的作用,规范继承这个类的程序员必须实现这个函数。


虚函数的常见错误

  • 虚函数常见的两个错误:无意的重写、虚函数签名不匹配
    1. 无意的重写:在派生类中声明了一个与基类的某个虚函数具有相同签名的成员函数,不小心重写了这个虚函数。
    2. 虚函数签名不匹配,例如:函数名,参数列表,const属性
  • 针对上述情况,在C++ 11中增加了两个继承控制关键字
    • override:保证在派生类中声明的重载函数,与基类的虚函数有相同的签名

      当派生类函数声明加了override修饰,则明确表示该函数为重写虚函数,如果该函数与基类的虚函数签名不一致,编译器就会报错

    • final:阻止类的进一步派生 和 虚函数的进一步重写

      如果不希望某个类被继承,或不希望某个虚函数被重写,则可以在类名和虚函数后加上 final 关键字,加上 final 关键字后,再被继承或重写,编译器就会报错。因此,一旦一个虚函数被声明为final,则派生类不能再重写它


静态关联
静态关联 (static binding) 指通过对象名调用虚函数. 在编译时即可确定其调用的虚函数属于哪一类

动态关联
动态关联 (dynamic binding) 是指通过基类指针与虚函数, 在运行阶段确定关联关系. 动态关联提供动态的多态性, 即运行阶段的多态性.

  1. 每个有虚函数的类都会为该类创建一个虚函数表,且在编译时已经决定了
  2. 虚函数表属于类而不属于对象
  3. 只要父类中有虚函数,子类就会有虚函数表,且父类虚函数表和子类虚函数表不同

参考文献:
C++虚函数_哔哩哔哩_bilibili
C++中的虚函数详解_爱笑的七小沐的博客-CSDN博客_c++虚函数
# 图解虚函数的内存模型和继承方式,虚函数表指针、虚函数表、多继承、多重继承、菱形继承、虚继承

comments powered by Disqus