多态与虚函数、虚函数表、对象的内存模型的思考

我在这就不详细说多态、虚函数是什么了,简单理解为:

        1.基类定义虚函数

        2.派生类重定义/重写(override)基类的虚函数

        3.基类指针(引用)指向(绑定)到派生类对象

        4.基类指针(引用)调用虚函数

就可以实现 基类指针(引用)有多种形态

想通过一个代码案例去进行分析,代码来自C++虚函数表剖析 | Leo的技术分享,下面的图片也是参考的这个文章:

#include <iostream>
using namespace std;

class A {
public:
    virtual void vfunc1() { cout << "A::vfunc1" << endl; }
    virtual void vfunc2() { cout << "A::vfunc2" << endl; }
    void func1() { cout << "A::func1" << endl; }
    void func2() { cout << "A::func2" << endl; }
private:
    int m_data1, m_data2;
};

class B : public A {
public:
    virtual void vfunc1() { cout << "B::vfunc1" << endl; }
    void func1() { cout << "B::func1" << endl; }
private:
    int m_data3;
};

class C: public B {
public:
    virtual void vfunc2() { cout << "C::vfunc2" << endl; }
    void func2() { cout << "C::func2" << endl; }
private:
    int m_data1, m_data4;
};

int main() {
    A a;
    B b;
    C c;
    A* pa = &a;
    A* pb = &b;
    A* pc = &c;
    B* pb2 = &c;

    pa->vfunc1();
    pa->vfunc2();

    pb->vfunc1();
    pb->vfunc2();

    pc->vfunc1();
    pc->vfunc2();

    pb2->vfunc1();
    pb2->vfunc2();

    return 0;
}

输出为:

A::vfunc1
A::vfunc2
B::vfunc1
A::vfunc2
B::vfunc1
C::vfunc2
B::vfunc1
C::vfunc2

首先,只要类中有虚函数(不管是继承的、还是重写的、或者是新建的),都会有虚函数表,而虚函数表存在于类的内存布局中,当类实例化对象的时候,会在对象的内存布局前加上一个虚函数表指针,用来指向虚函数表

对于上面中的类A,其虚函数表为:(注意,虚函数表是位于类的,而不是某个具体的对象)

虚函数表 A::vtable
-------------------
0: A::vfunc1
1: A::vfunc2

注意,对于非虚函数,其实就是代码段,是类所有的对象共享的,而数据成员是属于某个特定对象的。

所以对于对象a,其内存布局为:

// 基类 A 的内存模型
|-----------------|
|  A::vtable ptr  |  (指向 A::vtable)
|-----------------|
|   m_data1       |
|   m_data2       |
|-----------------|

其中A::vtable ptr为虚函数表指针,指向虚函数表。

接着看类B,虚函数表为:

虚函数表 B::vtable (继承自 A::vtable)
-------------------
0: B::vfunc1
1: A::vfunc2

其中B::vfunc1是类B重写(override)的虚函数,而A::vfunc2是类B继承自类A的,未进行重写。

对于对象b,其内布局为:

// 派生类 B 的内存模型
|-----------------|
|  B::vtable ptr  |  (指向 B::vtable)
|-----------------|
|   m_data1 (A)   |
|   m_data2 (A)   |
|   m_data3 (B)   |
|-----------------|

接着看类C,虚函数表为:

虚函数表 C::vtable (继承自 B::vtable)
-------------------
0: B::vfunc1
1: C::vfunc2

其中C::vfunc2是类重写(override)的虚函数(离C最近的继承->类B),而B::vfunc1是类C继承自类B的,未进行重写。

这里需要注意,不是要把重写的虚函数放到继承的虚函数前,虚函数表内的虚函数指针布局是根据函数申明顺序来的!

对于对象c,其内布局为:

|-----------------|
|  C::vtable ptr  |  (指向 C::vtable)
|-----------------|
|   m_data1(A)    |
|   m_data2(A)    |
|   m_data3(B)    |
|   m_data1(C)    |
|   m_data4(C)    |
|-----------------|

注意C中有2个m_data1,一个继承来自类A,一个为C自己定义的。

总的来说:“对象的虚表指针用来指向自己所属类的虚表,虚表中的函数指针会指向其继承的最近的一个类的虚函数

参考:

C++虚函数表剖析 | Leo的技术分享

Chapter12:多态——从虚函数表到RTTI(二) - 知乎

虚函数表存储的位置(解析C++内存分配及其编译分段)_c++虚表存在哪里-CSDN博客

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值