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

马谦马谦马谦 C/C++评论1,658字数 1101阅读3分40秒阅读模式

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

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

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

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

一、证明vptr指针存在

没有虚函数存在时:

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

程序会输出:

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

二、vptr指针的工作原理

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

 

多态的原理:vptr指针和vtable虚函数表-图片1

2.1 静态联编和动态联编

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

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

2.2 vptr绑定的顺序

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

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

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

带调试模式编译:

启动gdb:

然后运行程序:

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

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

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

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

// todo

 最后更新:2018-5-4
马谦马谦马谦
  • 本文由 马谦马谦马谦 发表于 2018年4月10日22:37:12
  • 转载请务必保留本文链接:https://www.dyxmq.cn/program/code/c-cpp/the-vptr-pointer-and-vtable-in-polymorphism.html
C/C++中struct和class的区别 C/C++

C/C++中struct和class的区别

一、class和struct的区别 C++中class和struct的区别: 继承权限,struct的默认继承权限为public,class的默认继承权限为private。 访问权限,struct的默...
指针和引用的区别 C/C++

指针和引用的区别

区别: 指针是一个变量类型,引用只是一个变量别名。 指针可以不用初始化,引用必须初始化。 指针可以指向空地址,引用不能指向空。 指针初始化后可以修改,引用不能修改。 其他: 引用本质上也是一个指针,内...
匿名

发表评论

匿名网友
:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:
确定

拖动滑块以完成验证