c++虚函数表

在C++中,虚函数(virtual functions)是一种支持多态(polymorphism)的机制。当一个基类中的成员函数被声明为虚函数时,它可以在派生类中被重写(override),并且通过基类指针或引用调用该函数时,实际执行的是派生类中的版本。为了实现这一机制,编译器会使用一种称为“虚函数表”(vtable)的数据结构。

虚函数表(vtable)

虚函数表是一个包含指向虚函数地址的指针数组。每个包含虚函数的类都有一个对应的虚函数表。当创建一个类的对象时,该对象会有一个隐藏的指针(通常称为vptr,即虚表指针),指向其所属类的虚函数表。这个vptr是隐式的,用户代码中不可见。

工作原理

  1. 类定义:当你定义一个包含虚函数的类时,编译器会为这个类生成一个虚函数表。
  2. 对象实例化:当你创建一个该类的对象时,对象内部会包含一个指向相应虚函数表的指针。
  3. 虚函数调用:当你通过基类指针或引用来调用虚函数时,程序会根据对象的实际类型,通过vptr找到正确的虚函数表,并从那里获取虚函数的地址来执行。

示例

下面是一个简单的示例,展示了虚函数和虚函数表的工作原理:

#include <iostream>

class Base {
public:
    virtual void show() {  // 基类中的虚函数
        std::cout << "Base::show()" << std::endl;
    }

    virtual ~Base() {}  // 虚析构函数
};

class Derived : public Base {
public:
    void show() override {  // 派生类中重写虚函数
        std::cout << "Derived::show()" << std::endl;
    }
};

int main() {
    Base* basePtr = new Derived();  // 基类指针指向派生类对象
    basePtr->show();  // 通过基类指针调用虚函数
    delete basePtr;  // 通过基类指针删除派生类对象,需要虚析构函数

    return 0;
}

在这个例子中:

  • Base 类有一个虚函数 show() 和一个虚析构函数 ~Base()
  • Derived 类继承自 Base 并重写了 show() 函数。
  • 在 main 函数中,我们创建了一个 Derived 对象,并使用 Base 类型的指针指向它。
  • 当通过 basePtr 调用 show() 时,由于 show() 是虚函数,实际调用的是 Derived 类中的 show() 版本。

虚函数表的内容

假设 BaseDerived 类的虚函数表如下所示:

  • Base 的虚函数表可能包含:

    • Base::show
    • Base::~Base
  • Derived 的虚函数表可能包含:

    • Derived::show (覆盖了 Base::show)
    • Base::~Base (保持不变)

每个 BaseDerived 对象都会有一个指向其对应虚函数表的 vptr。

为了更好地理解类A、B和C的虚函数表结构,我们可以绘制一个简单的结构图来表示。假设我们有如下定义:

class A {
public:
    virtual void funcA() { /* ... */ }
    virtual ~A() {}  // 确保A是多态的
};

class B {
public:
    virtual void funcB() { /* ... */ }
    virtual ~B() {}  // 确保B是多态的
};

class C : public A, public B {
public:
    void funcA() override { /* 重写A::funcA */ }
    virtual void funcC() { /* 新的虚函数 */ }
    virtual ~C() {}  // 确保C是多态的
};

基于这个定义,下面是一个简化后的结构图,展示类A、B和C的虚函数表(vtable)以及对象实例中的虚函数指针(vptr)。

虚函数表结构图

+-----------------+       +-----------------+
|   vtable for A  |       |  vtable for B   |
|-----------------|       |-----------------|
|  &A::funcA      |       |  &B::funcB      |
|  &A::~A         |       |  &B::~B         |
+-----------------+       +-----------------+
            ^                   ^
            |                   |
            |                   |
+-----------------+       +-----------------+
|  vptr (for A)   |       |  vptr (for B)   |
|-----------------|       |-----------------|
|  vtable for C   |       |  vtable for C   |
|  (from A)       |       |  (from B)       |
+-----------------+       +-----------------+
            \                   /
             \                 /
              \               /
               \             /
                +-------------+
                |  vtable for C  |
                |-----------------|
                |  &C::funcA     |  // 重写了A::funcA
                |  &B::funcB     |  // 继承自B
                |  &C::funcC     |  // 新的虚函数
                |  &C::~C        |  // 析构函数
                +-----------------+

对象实例结构图

对于类C的一个实例c,其内存布局可能如下所示:

+-----------------+  +-----------------+
|  vptr (for A)   |  |  vptr (for B)   |
|-----------------|  |-----------------|
|  vtable for C   |  |  vtable for C   |
|  (from A)       |  |  (from B)       |
+-----------------+  +-----------------+
            \                   /
             \                 /
              \               /
               \             /
                +-------------+
                |  Data for C  |  // 类C的数据成员
                +-------------+

在这个例子中:

  • c有两个vptr,一个指向从A继承来的部分,另一个指向从B继承来的部分。
  • 每个vptr都指向C自己的虚函数表,但是虚函数表的内容根据基类的不同而不同。
  • 如果通过A的引用或指针调用funcA(),会使用第一个vptr找到对应的虚函数表,并执行C::funcA。
  • 如果通过B的引用或指针调用funcB(),会使用第二个vptr找到对应的虚函数表,并执行B::funcB。
  • funcC()是C特有的,因此只能通过C的引用或指针直接访问。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值