c++虚函数表

类的大小

对类求大小实际上即是对类的实体对象求大小,其大小大于或等于所有非静态成员大小的总和(静态成员大小不计入,因为静态成员变量不在对象中存储),超出部分的来源:

  • 指向虚函数表的指针vptr,虚函数本身不占空间
  • 内存对齐(编译器优化)

说明:虚函数、成员函数(包括静态与非静态)和静态数据成员都不占用类对象的存储空间,与构造和析构无关!

对一个空类Base求sizeof的大小时,sizeof(Base)=1,因为c++标准规定类的大小不为0,空类的大小为1

class Base
{
}
//sizeof(Base)=1

class Base
{
    virtual void f(){}
}
//sizeof(Base)=8

虚函数表

同属于一个类的对象共享虚函数表,但有各自的vptr;虚函数表的实质是一个指针数组,里面存的是虚函数的函数指针

  • vptr指针在对象内存中存放在开始或末尾,视编译器而定。只需判定对象的地址与对象第一个成员的地址是否相等
#include<iostream>

using namespace std;

class Base
{
    public:
        virtual void f() {cout<<"base::f"<<endl;}
        virtual void g() {cout<<"base::g"<<endl;}
        virtual void h() {cout<<"base::h"<<endl;}
};

class Derive : public Base
{
    public:
        void g() {cout<<"derive::g"<<endl;}
};

typedef void(*Func)(void);

int main(int argc,char* argv[])
{
    //这里对象内存仅有指向虚函数表指针的指针,因此取地址直接可以得出该指针
    Base iBase;
    Base* piDerive = new Derive();

    long* ppBasevptr = (long*)&iBase;//指向虚函数表指针的指针
    long* pBasevptr = (long*)*ppBasevptr;//虚函数表指针

    Func f = (Func)pBasevptr[0];
    Func g = (Func)pBasevptr[1];
    Func h = (Func)pBasevptr[2];
    f();
    g();
    h();

    std::cout << "Basevptr:" << pBasevptr << std::endl;

    long* ppDerivevptr = (long*)piDerive;
    long* pDerivevptr = (long*)*ppDerivevptr;

    Func ff = (Func)pDerivevptr[0];
    Func gg = (Func)pDerivevptr[1];
    Func hh = (Func)pDerivevptr[2];
    ff();
    gg();
    hh();

    std::cout << "Derivevptr:" << pDerivevptr << std::endl;
    return 0;
}

/*result:
base::f
base::g
base::h
Basevptr:0x601db0
base::f
derive::g
base::h
Derivevptr:0x601d70
结论:只有被继承的虚函数虚函数表才被覆盖*/

参考

c++虚函数表分析

探索c虚函数在g中的实现

虚函数在虚拟存储器中的位置:
说明:当调用虚函数时,首先通过位于栈区的实例的指针找到位于堆区中的实例地址,然后通过实例内存开头处的vptr找到位于.rodata段的vtbl,再根据偏移量找到想要调用的函数地址,最后跳转到代码段中的函数地址执行目标函数

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值