LiSheng's blog LiSheng's blog
首页
笔记
个人简历
随笔集
GitHub (opens new window)
首页
笔记
个人简历
随笔集
GitHub (opens new window)
  • golang

  • cplus

    • 内存相关

    • 面向对象

      • 相关问题
      • 构造、析构顺序
      • 对象内存布局
      • 静态、动态多态
      • 虚函数表
        • 重载、重写、覆盖
        • 构造函数不能是虚函数
        • CRTP模式的原理和用途
        • 移动语义move
        • 构造函数初始化列表
        • 静态成员变量和函数
        • 多重继承
        • 菱形问题与虚继承
        • 对象池
      • STL相关

      • 内置数据结构
      • 数据结构示例
      • go和c++对比
      • 关键字

    • leetcode

    • 存储技术

    • 分布式系统

    • 计算机网络

    • Linux操作系统

    • Redis

    • 其他

    • 笔记
    • cplus
    • 面向对象
    lisheng
    2024-09-10
    目录

    虚函数表

    在C++中,虚函数表(vtable) 是实现运行时多态性(即动态多态性)的关键机制。它使得基类指针或引用可以调用派生类的重写方法,而无需在编译时确定实际调用哪个函数。这种机制在对象模型中发挥着重要作用。

    # 一、虚函数表(vtable)

    虚函数表是由编译器自动生成的一个数据结构,存在于包含虚函数的类中。它本质上是一个函数指针数组,存储了该类的所有虚函数的地址。

    • vtable的内容:对于一个包含虚函数的类,编译器会为该类生成一个虚函数表。表中存储了指向该类虚函数的指针。如果派生类重写了基类的虚函数,派生类的虚函数表中相应位置会被更新为指向派生类重写函数的指针。

    # 二、虚函数表指针(vptr)

    • vptr 是每个对象中隐藏的指针,它指向该对象所属类的虚函数表(vtable)。通常,这个指针是对象的一部分,存储在对象内存布局的最前面。
    • 当一个对象被创建时,vptr被初始化为指向该对象所属类的vtable。
    • 在调用虚函数时,通过vptr查找到对象所属类的vtable,然后通过vtable找到对应的虚函数指针并调用。

    # 三、虚函数表的工作原理

    当一个类包含虚函数时,编译器为该类生成虚函数表,类的每个对象都有一个vptr指向它所属的虚函数表。通过这个机制,可以实现动态绑定。

    # 示例:

    #include <iostream>
    
    class Base {
    public:
        virtual void func1() {
            std::cout << "Base func1" << std::endl;
        }
        virtual void func2() {
            std::cout << "Base func2" << std::endl;
        }
        virtual ~Base() = default; // 虚析构函数
    };
    
    class Derived : public Base {
    public:
        void func1() override {
            std::cout << "Derived func1" << std::endl;
        }
        void func2() override {
            std::cout << "Derived func2" << std::endl;
        }
    };
    
    int main() {
        Base* b = new Derived();
        b->func1();  // 调用 Derived::func1
        b->func2();  // 调用 Derived::func2
        delete b;    // 调用 Derived 析构函数,然后是 Base 析构函数
        return 0;
    }
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30

    解释:

    • 当 Derived 类对象被创建时,Derived 对象的 vptr 会被设置为指向 Derived 类的虚函数表。
    • 虚函数表 vtable 包含指向 Derived::func1 和 Derived::func2 的指针,而不是 Base::func1 和 Base::func2。
    • 当通过基类指针 b 调用虚函数 func1() 和 func2() 时,实际调用的是 Derived 类的实现。

    # 四、虚函数表的特性和注意事项

    1. 指针开销:每个包含虚函数的类对象会多一个 vptr 指针,这会增加对象的内存开销。

    2. 性能开销:通过虚函数表实现动态绑定会稍微降低性能,因为需要通过 vptr 查找函数指针,而不是直接调用函数。

    3. 虚析构函数:如果一个类包含虚函数,通常应将析构函数定义为虚函数,以确保通过基类指针删除派生类对象时,派生类的析构函数能够被正确调用。

    4. 多重继承:在多重继承中,每个基类的虚函数表都会生成独立的vtable,派生类对象中可能会有多个vptr来指向不同基类的vtable。

    # 五、虚函数表的布局(图解)

    以 Base 和 Derived 为例,虚函数表的布局可能如下所示:

    • Base 类的 vtable:

      +------------------+
      | Base::func1()    |
      +------------------+
      | Base::func2()    |
      +------------------+
      | Base::~Base()    |
      +------------------+
      
      1
      2
      3
      4
      5
      6
      7
    • Derived 类的 vtable:

      +------------------+
      | Derived::func1() |
      +------------------+
      | Derived::func2() |
      +------------------+
      | Derived::~Base() | // 如果没有重写,仍指向Base的析构函数
      +------------------+
      
      1
      2
      3
      4
      5
      6
      7
    • 对象中的 vptr:

      • Base 对象的 vptr 指向 Base 类的 vtable。
      • Derived 对象的 vptr 指向 Derived 类的 vtable。

    # 总结

    虚函数表是C++中实现运行时多态性的核心机制,通过vtable和vptr,C++能够在运行时根据对象的实际类型动态调用合适的函数。理解虚函数表及其工作原理对于掌握C++面向对象编程和调试复杂的多态性代码非常重要,也经常出现在C++的面试中。

    编辑 (opens new window)
    上次更新: 2024/09/13, 11:59:12
    静态、动态多态
    重载、重写、覆盖

    ← 静态、动态多态 重载、重写、覆盖→

    最近更新
    01
    ceph分布式存储-对象存储(RGW)搭建
    10-27
    02
    ceph分布式存储-集群客户端连接
    10-27
    03
    ceph分布式存储-管理crushmap
    10-27
    更多文章>
    Theme by Vdoing
    • 跟随系统
    • 浅色模式
    • 深色模式
    • 阅读模式