跳至主要內容

虚函数

张威大约 4 分钟c/c++多态

虚函数

虚函数的定义

虚函数就是在基类中被声明为virtual,并在一个或多个派生类中被重新定义的成员函数

// 类内部
class 类名 
{
    virtual 返回类型 函数名(参数表)
    {
        //...
    }
};
//类之外
virtual 返回类型 类名::函数名(参数表)
{
    //...
}

派生类对虚函数的重定义

如果一个基类的成员函数定义为虚函数,那么它在所有派生类中也保持为虚函数,即使在派生类中省略了virtual关键字,也仍然是虚函数

当一个基类中设有虚函数,而一个派生类继承了该基类,并对虚函数进行了重定义(重写函数体),我们称之为覆盖(override). 这里的覆盖指的是中相应被覆盖

重定义的格式要求:

  • 与基类的虚函数有相同的参数列表返回类型
class Base 
{
public:
    virtual void display()
    {   
        cout << "Base::display()" << endl;  
    }
    
    virtual void print()
    {
        cout << "Base::print()" << endl;    
    }
};
class Derived
: public Base 
{
public:
    virtual void display()
    {   
        cout << "Derived::display()" << endl;   
    }
};
void test(Base *pbase)
{
    pbase->display();
}
int main()
{
    Base base;
    Derived derived;
    test(&base);
    test(&derived);
    return 0;
}

上面的例子中,对于test()函数,如果不管测试的结果,从其实现来看,通过类Base的指针pbase只能调用到Base类型的display函数;但最终的结果是25行的test调用,最终会调用到Derived类的display函数,这里就体现出虚函数的作用了,这是怎么做到的呢,或者说虚函数底层是的怎么实现的呢?

虚函数的实现机制🍗🍗🍗

虚函数的实现是怎样的呢?简单来说,就是通过一张虚函数表(Virtual Fucntion Table)实现的。

具体地讲,当类中定义了一个虚函数后,编译器会在该类创建的对象的存储布局的开始位置产生一个虚函数指针(vfptr),该虚函数指针指向了一张虚函数表,而该虚函数表就像一个数组,表中存放的就是各虚函数的入口地址。如下图

虚函数描述、特点:

  1. 一个类里面定义了虚函数,那么,编译器需类型产生一个vftable虚函数表。虚函数表中主要存储的内容就是。当程序运行时,每一张虚函数表都会加载到内存的
    • 在编译时,一个类的虚函数表就确定了,这也是为什么它放在了只读数据段
  2. 一个类里面定义了虚函数,那么这个类定义的对象,其,内存中开始部分,多存储一个vfptr虚函数指针,指向相应类型的虚函数表vftable。一个类型定义的
  3. 一个类里面
  4. 如果派生类中的方法和基类继承来的某个方法,返回值、函数名、参数列表都相同,而且基类的方法是virtual虚函数,那么派生类的这个方法自动处理成虚函数,即覆盖关系。

虚函数机制是如何被激活的呢,或者说动态多态是怎么表现出来的呢?

从上面的例子,可以得出结论:

  1. 基类定义虚函数
  2. 派生类重定义(覆盖、重写)虚函数
  3. 创建派生类对象
  4. 基类的指针指向派生类对象
  5. 基类指针调用虚函数