多态的原理:vptr指针和vtable虚函数表

马谦马谦马谦 2018年4月10日22:37:12 发表评论
文章最后编辑于:2018-5-4 23:22:28

多态是C++中的重要内容,也是设计模式的基础。

形成多态的几个基本条件为:

  • 继承和虚函数
  • 父类对象指向子类对象

多态形成的原理就是vptr指针和vtable虚函数表,当一个类中有虚函数时,编译器就会自动生成虚函数表,并生成一个vptr指针指向这个虚函数表。调用虚函数的时候,会通过这个vptr指针找到相应的虚函数表,然后再定位到对应的函数,以此来调用形成多态。

一、证明vptr指针存在

没有虚函数存在时:

A和B的大小都是1(C++对空类会分配一个字节的内存),但是将A中的print()函数设置为虚函数后:

程序会输出:

因为添加虚函数后,系统会默认添加vptr指针,32位系统,指针是四个字节,所以A和B的大小就变成了4

二、vptr指针的工作原理

在创建一个含有虚函数的类时,系统会自动生成一个虚函数表,存放了当前对象所有的虚函数,而vptr指针就指向这个表的地址。子类继承父类后,也会生成一个属于自己的虚函数表和vptr指针。对象调用虚函数,则会通过vptr指针在虚函数表中查找函数的地址进行调用。以此来达到多态的效果。

 

多态的原理:vptr指针和vtable虚函数表

2.1 静态联编和动态联编

说到多态肯定免不了要提到动态联编,所谓动态联编就是指程序在运行时才能决定的运行片段,例如ifswitch代码段,它们只有在运行时再能知道下一步是什么,走哪个代码代码段。多态也是如此,运行到了虚函数的时候才能决定执行哪个函数。

而静态联编就是指程序在编译阶段就能决定的事情,就像我们的main函数,编译后就固定走这一条路线。

2.2 vptr绑定的顺序

子类中vptr指针的值的绑定顺序:

  1. 运行父类构造函数时,先指向父类的虚函数表。
  2. 运行子类构造函数时,再把指针指向子类的虚函数表。

我们可以使用gdb来验证这一个观点,代码:

带调试模式编译:

启动gdb:

然后运行程序:

上面很明显就能看到,b在执行父类构造的时候时指向父类的虚函数表,在执行自己的构造函数时才转向B的

实际上子类对象vptr指针的赋值可以分为以下两步:

  1. 执行父类构造函数时,指向父类的虚函数表多态的原理:vptr指针和vtable虚函数表
  2. 执行子类的构造函数时,再指向子类的虚函数表。多态的原理:vptr指针和vtable虚函数表

三、关于虚函数的常见问题

// todo

本文共执行45次查询,耗时0.273秒!
马谦马谦马谦

发表评论

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen: