虚函数和虚析构函数的实现原理

1.总结虚函数的实现原理:





当类中有虚函数或者虚析构函数时,在实例化类的对象时,对象内存中除了成员变量的大小,还有一个虚函数表指针,而且虚函数表指针放在内存的最前面,虚函数表指针会指向一个虚函数表,而以为Shape类中含有虚函数,这个虚函数表将于Shape类的定义同时出现,在计算机中虚函数表也是占用一定到的内存空间的,且虚函数表由于一旦产生就具有不变性,所以编译器就会经量把它放到稳定(或者说是只读)的内存区。虚函数表vtable在Linux/Unix中存放在可执行文件的只读数据段中(rodata)。

在上例中虚函数表的起始位置是0xCCFF,那么虚函数表指针的值就是0xCCFF,每个类只有一张虚函数表,所有类的对象都共用同一张虚函数表。所有对象都含有相同的虚函数表指针值0xCCFF,以确保所有的对象含有的虚函数表指针都指向正确的虚函数表。虚函数表中存放的是所有的虚函数地址。计算时,先找到虚函数表指针,通过指针的偏移找到虚函数的指针,然后就可以调用虚函数。

上例图中,Circle派生自Shape,Circle从父类派生了虚函数,于是它也有了自己的虚函数表,这两个表的起始地址是不一样的,但是表中calcArea()函数的起始地址是一样的,这也就是说,两张不同的虚函数表中的函数指针可能指向同一个函数。

当Circle类定义了自己的虚函数,如下图所示,



由于此时,Circle类自己定义了calcArea()函数,所以将会覆盖掉父类的函数。


2.总结虚析构函数的是实现原理:

虚析构函数的特点是当将父类的析构函数声明为虚析构函数以后,再用父类的指针去指向子类的对象,并用delete去销毁父类的指针的时候,不会再只调用父类的析构函数,而是会先调用子类的析构函数再调用父类的析构函数,即会释放掉子类对象了,不会再因为子类对象得不到释放而产生内存泄露。这种情况也和虚函数表有关。实现过程如下:当声明父类析构函数为虚析构函数以后,在子类和父类的虚函数表中将都出现虚析构函数的函数指针,如下两幅图所示。当用父类的指针指向子类的对象,用delete Shape释放对象时,会通过Shape指针找到子类Circle的虚函数表指针,从而找到虚函数表,从而通过偏移找到虚析构函数的地址,从而调用子类Circle的析构函数,然后也会调用父类的析构函数。(为什么基类的析构函数是虚函数?

  在实现多态时,当用基类操作派生类,在析构时防止只析构基类而不析构派生类的状况发生


多态的实现原理如下

当用父类的指针去指向子类对象时,会拿到子类的虚函数表指针,然后找到虚函数表,通过虚函数表指针的偏移,找到要调用的虚函数的函数指针,从而实现函数的调用。注意这里的偏移必须是和父类的偏移量是一样的。

与本节内容有关,补充两个概念:函数的隐藏和覆盖

函数的隐藏:没有定义多态的情况下,即没有加virtual的前提下,如果定义了父类和子类,父类和子类出现了同名的                        函数,就称子类的函数把同名的父类的函数给隐藏了。

函数的覆盖:是针对多态来说的。如果定义了父类和子类,父类中定义了公共的虚函数,如果此时子类中没有定义同                       名的虚函数,那么在子类的虚函数表中将会写上父类的该虚函数的函数入口地址,如果在子类中定义了                        同名虚函数的话,那么在子类的虚函数表中将会把原来的父类的虚函数地址覆盖掉,覆盖成子类的虚函                         数的函数地址,这种情况就称为函数的覆盖。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值