virtual继承与虚函数

几个定义

1.虚基类偏移表(虚基表):一般在虚拟继承的时候出现(例如:Stu:virtual public Person),该表的第1行存的是this指针,第2行存储的时候虚基表指针与 Stu成员(Stu类中第一个声明的成员)的偏移量,如果虚继承的是虚函数表(基类有虚函数就先继承虚函数表,再按照成员变量的声明顺序继承放在内存中),偏移量就是虚基表指针(_vbPtr)与虚函数表指针(_vfPtr)的距离。

2.虚函数表:当基类有虚函数时,这些虚函数并不是存在栈中的与普通函数一样,存放在代码段,但与普通函数不同的是,在实例化的对象中,会有一个虚函数表指针(_vfPtr)存储在对象的内存中,一个类中的所以虚函数都会放入虚函数表中,而实例化出的对象包含的是_vfptr这个指针(4或8个字节), 指向的是一个函数指针数组(存储函数指针的数组)如图:

虚函数表本质是一个存虚函数指针的指针数组(_vfptr理解是函数指针数组的首元素地址),一般情况这个数组最后面放了一个nullptr(0x00 00 00 00)。

多态的机制

一般继承无虚函数覆盖

一般继承有虚函数覆盖

a虚函数表中的fun1()函数地址被重写为b虚函数fun1()的地址了 a的fun2()地址不变,b的fun2()地址接在新的虚函数表的后面。

像是在a的虚函数表上变化,有被重写的a的虚函数就改成b中对应的虚函数的地址,a中虚函数没有被重写的不变,b中虚函数没有重写的接在a 的虚函数表的后面,最后虚表指针改变作为b的虚函数指针。

多重继承无虚函数覆盖

继承的a的虚表中有4个地址,前2个是A类中的虚函数的地址,后2个是C类中的虚函数的地址可以看出,多重继承时,按声明顺序继承,C中没有发生重写的虚函数放在第一个声明的类的虚表中。

多重继承有虚函数覆盖

蓝色线指的是没有被重写的,可以看出,在c中继承的a和b的虚表中的fun1a()均被修改 c中原来的fun1a()发生了重写,而c中原来的fun3c()放入了a虚表中(0x 00 f4 14 0b)。

单一的一般继承

p  c  gc 内存都只有4个字节存储的时虚表指针,右边依次是p  c  gc 的虚表。

与上面的规则一样,重写了的就覆盖,没重写的基类的虚函数不变,没重写的派生类的虚函数接在第一个虚表的后面(这里就gc一个虚表)。

钻石型重复继承

钻石型虚拟多重继承

先看看b1的内存

在virtual下的继承,派生类有重写的虚函数会覆盖基类虚基表中对应的虚函数,派积类没有发生重写的虚函数不会接在基类的虚基表的后面(与非虚拟继承的差别),而是重新创建一个虚函数表。 在图中可以看出,b1的内存有12个字节,第一行是派生类中没有发生重写组成的虚表指针,第3行是继承的B的虚表,如果有重写会覆盖原虚函数。由前面成员变量的菱形继承可推第二行是虚基类 偏移表指针,结果也是如此。

b2同理

看看d的内存

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值