• C++中虚表是什么


    虚函数表,以及虚函数指针是实现多态性(Polymorphism)的关键机制。多态性允许我们通过基类的指针或引用来调用派生类的函数

    定义

    虚函数(Virtual Function)

    • 定义:类中使用virtual 关键字修饰的函数 叫做虚函数

    • 语法

    highlighter- cpp
    class Base {
    public:
        virtual void show() { cout << "Base show" << endl; }
    };

    虚函数表(Virtual Function Table)

    • 定义:当类含有至少一个虚函数时,编译器会为该类创建一个虚函数表。这个表是一个编译时构建的静态数组,存储了指向类中所有虚函数的指针。如果一个派生类重写了这些函数,那么在派生类的虚表中,相应函数的指针会被更新为指向派生类中的版本。
    • 作用v-table使得在运行时可以实现函数的动态绑定,允许通过基类的指针或引用调用正确的函数版本。

    虚函数指针(Virtual Pointer)

    • 定义:每个含有虚函数的类的对象(实例化出的)会持有一个指向相应虚表的指针,这个指针通常被称为虚指针(vptr)。vptr是对象运行时的一部分,确保了当通过基类指针调用虚函数时,能够查找到正确的函数实现。
    • 作用:在对象的生命周期开始时,构造函数会设置vptr以指向相应的虚函数表。如果有派生类对象,它的构造函数会更新vptr,以指向派生类的虚函数表。这保证了通过基类的引用或指针调用虚函数也会执行最派生类的重写版本。

    示例

    highlighter- cpp
    #include 
    using namespace std;
    
    class Base {
    public:
        virtual void func1() { cout << "Base::func1" << endl; }
        virtual void func2() { cout << "Base::func2" << endl; }
    };
    
    class Derived : public Base {
    public:
        void func1() override { cout << "Derived::func1" << endl; }
        // func2() 继承自 Base
    };
    
    void printVTable(void* obj) {
        cout << "vptr Address: " << obj << endl;
        void** vTable = *(void***)obj;
        cout << "VTable[0] (func1): " << vTable[0] << endl;
        cout << "VTable[1] (func2): " << vTable[1] << endl;
    }
    
    int main() {
        Base* base = new Base();
        Derived* derived = new Derived();
    
        cout << "Base object:" << endl;
        printVTable(base);
    
        cout << "\nDerived object:" << endl;
        printVTable(derived);
    
        delete base;
        delete derived;
    
        return 0;
    }
    

    程序输出如下,可以看到没用重写的func2函数地址是一样的。

    highlighter- apache
    Base object:
    vptr Address: 0x8c1510   
    VTable[0] (func1): 0x422270
    VTable[1] (func2): 0x4222b0
    
    Derived object:
    vptr Address: 0x8c1530   
    VTable[0] (func1): 0x422330
    VTable[1] (func2): 0x4222b0

    如下图所示:

    面试题

    (来自2024腾讯实习面试)场景题:一个类 A,里面有一个打印 helloworld 的虚函数,然后类 A 会在构造函数里调用这个虚函数,此时有个类 B,继承A,重写了这个 helloworld虚函数,问你在创建类 B 时,会打印 A 里的 helloworld 还是 B 里的。

    代码如下:

    highlighter- arduino
    class A {
    public:
        A() {
            print();
        }
        virtual void print() {
            cout << "A print" << endl;
        }
    };
    
    class B : public A {
    public:
        void print() override {
            cout << "B print" << endl;
        }
    };
    int main() {
        A* aTemp = new B();
        delete aTemp;
    }

    解答:基类构造函数执行的时候,派生类的部分尚未初始化,因此调用的虚函数不会下发到派生类中。

    最终会打印 A print,而不是类 B 里重写的版本


    __EOF__

  • 本文作者: 江水为竭
  • 本文链接: https://www.cnblogs.com/az1r/p/18081756
  • 关于博主: 评论和私信会在第一时间回复。或者直接私信我。
  • 版权声明: 本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
  • 声援博主: 如果您觉得文章对您有帮助,可以点击文章右下角推荐一下。
  • 相关阅读:
    Python之基本扩展模块
    设计模式学习笔记(二十)状态模式及其实现
    基于SSM架构的新闻管理系统设计与实现论文
    PriorityQueue优先级队列
    WebGL前言——WebGL相关介绍
    2020年09月 Python(二级)真题解析#中国电子学会#全国青少年软件编程等级考试
    Ajax模拟视频点赞功能
    with语句和上下文管理器
    信贷风控拒绝客户的捞回策略详解
    Spring复杂对象的3中创建方法
  • 原文地址:https://www.cnblogs.com/Az1r/p/18081756