C++中的Vitrual

Virtual虚函数

多态

  • 使用基类的指针分别调用重载函数时,调用的都是基类的函数。
  • 使用基类的指针分别调用虚函数时,调用的是本类的函数。
  • 基类指针强制类型转换后,虚函数调用原来的函数,普通函数调用本类的函数

结论:使用类指针调用成员函数时,普通函数由指针类型决定,虚函数由指针指向的实际类型决定

虚函数实现(内存布局)

每个类在创建的时候构造一张虚函数表,该表是编译器在编译时设置的静态数组,每个表项存放虚函数的入口地址(即函数指针),发生虚函数调用时,通过函数指针所指向的函数来调用相应虚函数。
vptr长8个字节,用*(unsigned long *)obj 取出后,因为虚表中每个元素(虚函数指针)在64位编译器下是8个字节,通过 *((unsigned long *)vptr_addr+offset)即可获得每个函数的表项地址。
在这里插入图片描述

在这里插入图片描述
可以看到子类对父类虚函数进行重载时,基类指针指向子类实例的内存的虚函数表中,原父类虚函数已被子类虚函数覆盖,实际调用时,调用的是子类的虚函数。(注意:父类基函数在子函数前面)

如存在多重继承,则子类中存在多个虚函数表,子类自己的虚成员函数添加到第一个声名的虚函数表中
实现过程小结:通过对象内存中的vptr找到虚函数表vtbl,接着通过vtbl找到虚函数实现区域并调用

构造函数无法定义虚函数

因为构造函数完成前,对象内存尚未初始化,vptr没有值,无法通过vptr找到虚函数实现代码区。

尽管构造函数不可以为虚函数,但是有些场景下我们确实需要 “Virtual Copy Constructor”。 “虚复制构造函数”的说法并不严谨,其只是一个实现了对象复制的功能的类内函数。 举一个应用场景,比如剪切板功能。 复制内容作为基类,但派生类可能包含文字、图片、视频等等。 我们只有在程序运行的时候才知道我们需要复制的具体是什么类型的数据。(例:在基类函数中声明virual Clone)

析构函数最好定义为虚函数

用基类类型指针绑定派生类实例,析构的时候,如果基类析构函数不是虚函数,则只会析构基类,不会析构派生类对象,从而造成内存泄漏。为什么会出现这种现象呢,析构的时候如果没有虚函数的动态绑定功能,就只根据指针的类型来进行的,而不是根据指针绑定的对象来进行,所以只是调用了基类的析构函数;如果基类的析构函数是虚函数,则析构的时候就要根据指针绑定的对象来调用对应的析构函数了。

通过基类定义虚函数重载,使派生类对象删除时调用子类的虚析构函数,并子类析构函数执行完后自动执行父类析构函数

虚函数成为私有函数

int main()必须声明为Base类的友元,否则编译失败。 编译器报错: ptr无法访问私有函数。 当然,如果把基类声明为public, 继承类为private,该问题就不存在了。

虚函数的同步

一旦某个成员函数在一个类中被声明为虚函数,在继承体系中他就永远是虚函数了

dynamic cast转型

父类转换为子类时,父类中要有虚函数:

  1. 父类指针指向子类对象,转换安全,返回类对象指针
  2. 父类对象指向父类对象,转换不安全,返回nullptr。
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值