C++ 虚函数表

虚函数
为了实现运行时多态,通过指向派生类的基类指针或引用调用派生类中同名覆盖函数。虚函数用法格式为:virtual 函数返回类型 函数名(参数表) {函数体}
虚函数表
虚函数调用是通过虚函数表简介访问的,就是说基类指针或引用指向派生类时,基类的虚函数表被重写。所以调用函数被覆盖。
含虚函数类的实例的第一个指针变量指向虚函数表,
class Base
{
virtual void Show(void) {cout << “Base::show” << endl;}
};

查看Base的实例会在栈中占用的空间
64位系统中 cout<<sizeof(Base)<<endl;输出8;这是一个指针需要的空间,指向虚函数表,虚函数表中存储有函数指针Show;
内存
只有实例才占用内存,抽象是不占用内存的。所以函数函数类的对象的首地址指向的空间放的是虚函数表地址。由于一个类的所有对象共用函数,所以含虚函数类的N+个实例对象指向同一个虚函数表。
我们可以通过指针调用函数。

class Base
{
public:
virtual void Show(void) {cout << "Base::show()" << endl;}
};
//函数指针类型fun
typedef void (*fun)(void);
Base base;
//base首地址做两次指针运算,指向第一个虚函数,指针运算前要强制转换类型
fun pf = ((fun)(&base));
pf();
输出Base::Show();
和base.show();输出一样。
函数没有访问类的属性成员,如果改为
````C++
class Base
{
public:
    Base(int i) Id(i) {}
    virtual void Show(void) {cout << "Base::show()" << "ID" << Id << endl;}
private:
    int Id;
};
//函数指针类型fun
typedef void (*fun)(void);
Base base(32);
//base首地址做两次指针运算,指向第一个虚函数,指针运算前要强制转换类型
fun pf = **((fun**)(&base));
pf();

输出ID值不正确,这是因为调用类的函数时,隐式的把对象指针this传递进去。
所以修改函数指针
typedef void (*fun)(Base & base);
调用
pf(base);
完整代码如下

#include<iostream>
using namespace std;
class Base
{
public:
    Base(int i):Id(i)   {}
    virtual void show() {cout<<"Base::show()"<<endl;}
    virtual void GetId() {cout<<"Base::Id:"<<Id<<endl;}
private:
    int Id;
};
class Child:public Base
{
public:
    Child(int i):Base(i),Id(i)    {}
    void show() {cout<<"Child::show()"<<endl;}
    void GetId()    {cout<<"Child::Id:"<<Id<<endl;}
private:
    int Id;

};
struct nullStruct//空类有一个占位
{

};

int main()
{
    Base base(23);
    Child child(34);
    Child child2(22);

    cout<<"sizeof(int):"<<sizeof(int)<<endl;
    cout<<"sizeof(child):"<<sizeof(Child)<<endl;
    out<<"sizeof(Base):"<<sizeof(Base)<<endl;  //结果为16,内存必须为最长变量的整数倍,即8的整数倍,
    cout<<"sizeof(nullStruct:"<<sizeof(nullStruct)<<endl;
    cout<<"*(long*)(&child )"<<*((long*)(&child))<<endl;    //虚表指针
    cout<<"*(long*)(&child2)"<<*((long*)(&child2))<<endl;
    cout<<"*(&base)"<<*((long*)(&base))<<endl;     //虚表指针

    typedef void (*pf_t)(Child &ch);

    cout<<"通过指针调用child.show()"<<endl;
    pf_t f  =**((pf_t**)(&child));
    f(child);

    cout<<"通过对象调用GetId()"<<endl;
    child.GetId();

    cout<<"通过指针调用第二个虚函数GetId()"<<endl;
    pf_t g = *(*(pf_t**)(&child) + 1);
    g(child);

  return 0;
}


输出:
![在这里插入图片描述](https://img-blog.csdnimg.cn/20191012214240158.jpeg)

虚基类继承,子类继承了父类的虚函数表指针成员,但是,和继承的父对象普通成员变量由父类构造初始化不同,当定义子对象时,这个虚表指针变量的值被子类的虚表指针覆盖。
另外,函数指针的值类似PC值。变量的指针加1则指针的值加变量长度大小,例如
int a;
int *pa = &a;
a的长度是固定的,4个字节
所以(long)(pa + 1) - (long)(pa)结果是4;
而函数的长度是不定的,所以(long)(pf + 1) - (long)(pf)结果是1;
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值