C++ 虚函数 从入门到精通

写在前面

在面向对象的程序设计中为什么会出现虚函数这个说法呢?
万物究其本源:
1.面向对象的三大特征:

  • 封装:类
  • 继承
  • 多态:动态绑定,当使用基类的引用(或者指针)对象调用一个虚函数时,将发生动态绑定,会根据对象本身的类型调用对应的函数

2.虚函数是在继承这一特征上出现的
继承指的是基类和派生类的层次关系,派生类可以直接或间接的继承基类。

  • 基类:定义在层次关系中,所有类共同拥有的数据成员
  • 派生类:定义各自特有的成员

在基类中定义的成员函数,如果在派生类中各自定义自己的版本,则在基类中将该函数定义成虚函数;派生类中如果将继承来的虚函数重新定义了,可以在形参列表后加上override关键字,来表明改写了基类的虚函数
在这里插入图片描述

虚函数

虚函数是通过虚函数表来实现的,更详细内容参考:详细内容

类派生列表

作用:指名派生类是从哪些基类继承而来的
位置:紧跟在派生类的类名后面,由 冒号 和 以逗号分隔的基类名组成,基类名前需指名继承的级别(private、protected、public)
基类:
在这里插入图片描述
派生类:
在这里插入图片描述
基类和派生类详解

  • 类内除构造函数之外的非静态成员函数都可以定义成虚函数

  • virtual关键字只可以出现在类内对该函数进行声明时,在类的外部定义该成员函数时不能出现

  • 当通过指针或者引用调用虚函数时,虚函数的解析过程发生在程序 运行时 而 不是在编译时,与其绑定的对象有关,只有这种情况会出现对象的静态类型和动态类型不一致的情况;普通对象调用虚函数或者非虚函数时,在编译时绑定到该对象所属类的函数上。

  • 派生类可以覆盖其基类中的虚函数,也可以不覆盖,如果不覆盖,默认继承基类中的版本

  • 派生类覆盖基类中的虚函数时,可以加上virtual关键字,也可以不加,因为一旦该函数声明成虚函数,在其所有的派生类中都是虚函数

  • 派生类中虚函数的返回类型 需要 和基类中虚函数的返回类型是一致的,但是当虚函数返回是该类的指针或者引用时,上述规则就无效。例如D是从B派生来的,B中的虚函数返回的是B*,D中对应的虚函数可以返回D*,但这时候要求从D到B的类型转化是可访问的

  • 派生类中虚函数的形参 要和基类中对应虚函数的形参严格匹配。如果派生类中的虚函数和基类中的基函数名字相同,但是形参列表不同,这时候,派生类中的虚函数并没有覆盖基类中的虚函数,而是新定义的函数,与基类中的同名虚函数相互独立。
    如果这种参数列表不匹配的情况,是写错了,编译器并不会报错,但是为了让编译器检查出来,可以加上override关键字,来指名该函数是覆盖基类中的基函数
    在这里插入图片描述

  • 如果虚函数不希望被派生类覆盖,则加上final关键字
    在这里插入图片描述

  • 如果对虚函数的调用不希望是动态绑定,则可以通过类作用域运算符来指定调用哪个类的虚函数
    在这里插入图片描述

纯虚函数

  • 在声明类的成员函数时,在 分号 前加上 =0 ,就表明该函数是纯虚函数,=0只能出现在函数的声明处
  • 纯虚函数如果要定义的话,必须要定义在类的外部 在这里插入图片描述
  • 含有纯虚函数的类称为抽象基类,不能定义该类的对象,即不能创建抽象基类的对象。在抽象基类的派生类中,必须覆盖纯虚函数,否则该派生类依然是抽象基类

实例

参考链接

#include <bits/stdc++.h>
#include <iostream> 
#include <stdlib.h>
using namespace std; 

class CA 
{ 
public: 
    void f() 
    { 
        cout << "CA f()" << endl; 
    } 
    virtual void ff() 
    { 
        cout << "CA ff()" << endl; 
        f(); 
    } 
}; 

class CB : public CA 
{ 
public : 
    virtual void f() 
    { 
        cout << "CB f()" << endl; 
    } 
    void ff() 
    { 
        cout << "CB ff()" << endl; 
        f(); 
        CA::ff(); 
    } 
}; 
class CC : public CB 
{ 
public: 
    virtual void f() 
    { 
        cout << "C f()" << endl; 
    } 
}; 

int main() 
{ 
    CB b; 
    CA *ap = &b; 
    CC c; 
    CB &br = c; 
    CB *bp = &c; 

    ap->f(); //CA f()
    cout << endl;

    b.f(); //CB f()
    cout << endl;

    br.f(); //C f()
    cout << endl;

    bp->f(); //C f()
    cout << endl;

    ap->ff(); //CB ff() CB f() CA ff() CA f()
    cout << endl;

    bp->ff(); //CB ff() C f() CA ff() CA f()
    cout << endl;

    return 0; 
}

运行结果:

CA f()

CB f()

C f()

C f()

CB ff()
CB f()
CA ff()
CA f()

CB ff()
C f()
CA ff()
CA f()

总结:

  • 如果指针或者引用对象所调用的函数是虚函数,那么调用它所指向 或者 绑定 的对象所属类的虚函数(动态绑定,在程序运行时确定所指向的类型)
  • 如果调用的是非虚函数,那么直接调用该指针类型或者引用类型所属类的虚函数(静态绑定,在编译时就确定了该指针或引用指向对象的类型)特别注意 第一行的输出 ap->f(); //CA f()
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章