C++跟我一起透彻理解虚函数表

本文深入探讨了C++中类的构造顺序、虚基类与虚函数的作用,通过实例展示了如何理解类继承与虚表的概念,以及如何在多继承场景下管理虚表指针。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

//首先让我们来了解类对象的构造顺序。
#include <iostream>
using namespace std;
class A
{
public:
    A(){ cout << "A" << endl; }
    virtual void PrintfA() = 0;
};
class B 
{
public:
    B(){ cout << "B" << endl; }
};
class C :virtual public A , public B
{
    //关于构造对象顺序:
    //第一步先构造虚基类,例如此处的虚继承。
    //第二步构造一般继承类,此处从左往右顺序执行。
    //第三部构造成员变量,记住这里的成员变量的构造是顺序执行的。
    //第四构造自身的构造函数。
public:
    C()
    { cout << "C" << endl; }
    void PrintfA()
    {
        cout << "hello word!!" << endl;
    }
private:
};
int main()
{
    C c;
    return 0;
}

这里写图片描述

#include <iostream>
using namespace std;

typedef void (*Pfun)();
class Base
{
public:
    virtual void Printf()
    {
        cout << "Base::Printf()" << endl;
    }
    void PrintfBae()
    {
        cout << "Base::PrintfBase" << endl;
    }
};
class Son1:public Base
{
public:
    virtual void Printf()
    {
        cout << "Son1::Printf()" << endl;
    }
    void PrintfSon1()
    {
        cout << "Son1::PrintfSon1()" << endl;
    }
};
class Son2 : public Son1
{
public:
    void Printf()
    {
        cout << "Son2::Printf()" << endl;
    }
    virtual void hello()
    {
        cout << "hello" << endl;
    }
    void PrintfSon2()
    {
        cout << "Son2::Printf()" << endl;
    }
};
int main()
{
    Son2 s2;
    Base &b = s2;
    //b.Printf();
    //此处可以得出这三个类维护了一个共同的虚表,但是我们还无法知道他们的
    //具体位置以及各个类里面非虚函数的存储关系,所以我们接下来验证。
    //Pfun fun = (Pfun)*((int *)(&(int*)&b)+1);
    Pfun fun = (Pfun)*((int *)*(int *)(int *)(&b) + 0);
    //这种做法基本没有人解释,所以我在后面用图解释了。
    fun();
    fun = (Pfun)*((int *)*(int *)(int *)(&b) + 1);
    fun();
    //--------------------------------------------------------
    fun = (Pfun)*((int *)*(int *)(int *)(&s2) + 0);
    fun();
    fun = (Pfun)*((int *)*(int *)(int *)(&b) + 1);
    fun();
    //打印结果与上面一样,可以得出这份虚表是所有继承该拥有虚表基类的成员类所共享的。
    //每个对象的产生必定有两个数据,一个vptr指向虚表的指针,还有其自身的成员
    //变量。
    return 0;
}
//总结1:前提是基类是有虚表的,单一继承,所有的成员类与基类共同维护同一个虚表,各自有
//一个vptr指向这个虚表,子类如果会对虚表进行替换修改,具体会根据子类是否重载了基类的
//虚函数来确定,我们成为覆盖。多继承下面讨论,其实本质是一样的,关键问题是,多继承中
//子类中到底有几份虚vptr指针。

这里写图片描述

#include<iostream>
using namespace std;

class A
{
public:
    virtual void Printf()
    {
        cout << "A::Printf()" << endl;
    }
    virtual void PrintfA()
    {
        cout << "A::PrintfA()" << endl;
    }
};
class B
{
public:
    virtual void PrintfB()
    {
        cout << "B::Printf()" << endl;
    }
    virtual void Printf()
    {
        cout << "B::Printf()" << endl;
    }
};
class C : public A, public B
{
public:
    void Printf()
    {
        cout << "C::Printf()" << endl;
    }
};

int main()
{
    typedef void(*PFun)();
    C c;
    PFun fun = (PFun)*((int *)*(int *)((int *)(&c)+0) + 0);//第一个虚表指针vptr。
    fun();//此处覆盖了,调用的是C中的Printf();
    fun = (PFun)* ((int *)*(int *)((int *)(&c) + 1) + 0);//第二个虚表指针vptr。
    fun();
    //此处还有一个亮点,就是为什么(int *)(int *)两次,这下你应该明白了吧,因为可能
    //有多个虚指针,编译器做的很好了,如果不这样做编译会报错。
    cout << sizeof(c) << endl;//终极测试结果8。
    return 0;
}

//总结2:多继承中每一个基类都维护一张自己的虚表,而子类中每继承一个虚基类就会有存在一个
//指向该基类虚表的指针,以上代码测试结果是C对象中有两个vptr指针,一个指向A基类的虚表,一
//个指向B基类的虚表。

这里写图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值