🍐05-C++面试之多态底层实现:虚表和虚表指针
0 前述
针对于自己在秋招的面试中,对于CPP
部分遇到的问题,其中大部分是以此为起点,你可以基于这些点,将自己对于CPP
学习的知识点,串联起来。无论面试官,问这一类问题中的某个点,你都应该可以将这一个珠子串联到自己的知识链上来讲。这是一种拓展知识的能力。
在此专栏下面个人校招记录:回馈牛客,对CPP
做一个小小的总结。
本部分关于CPP
多态的底层实现的总结,从虚表和虚表指针的底层原理和调用流程,以及关于多态的经典案例来展开。
- 下面对应的是之前发布的个人校招其他公司面试总结,希望可以更好的帮到你
这里是CPP
一些面试问题整理
1 多态的作用
大致总结:在基类的函数前加virtual
关键字,可以在派生类中可以重写该函数。
通过运行时动态绑定,根据所指对象的实际类型来调用相应的函数。
1)如果对象类型是派生类,就调用派生类成员函数;
2)如果对象类型是基类,就调用基类的成员函数。
2 多态性底层基础
底层原理
1)虚表vtable
:virtual function table
:当类中含有virtual关键字修饰的方法时,编译器会自动为该类生成虚表;
2)虚表指针vptr
:在含有虚函数的类实例化对象时,对象地址的前四个字节存储着指向虚表的指针。
调用流程
1)编译器在发现基类中有虚函数时,会自动为每一个含有虚函数的类生成一份虚表,该表是一个一维数组,表里的元素是虚函数的入口地址。
2)同时,编译器会在每个对象的前四个字节保存一个虚表指针vptr
,其指向所属类的虚表。在构造时,根据对象的类型去初始化虚表指针vptr
,让vptr
指向正确的虚表,使得在调用虚函数时,能找到正确的函数。
3)**【关键】**所谓的合适时机,在派生类实例化对象时,程序会自动调用构造函数,在构造函数中创建虚表,并对虚表初始化。在构造派生类的对象时,会先调用基类的构造函数,此时编译器只“看到了”基类,并且为基类对象初始化虚表指针,令它指向基类的虚表;当调用派生类的构造函数时,接着为派生类对象初始化虚表指针,令它指向派生类的虚表。
4)当派生类对基类的虚函数没有覆盖(override),派生类的虚表指针直接指向基类的虚表;当派生类对基类的虚函数**有覆盖(override)**时,派生类的虚表指针指向自己的虚表;当派生类中有自己的虚函数时,将该虚函数地址添加在自己的虚表里面。
5)以上步骤总结:这样指向派生类的基类指针在运行时,就可以根据派生类对虚函数重写请况,进行动态调用,从而实现多态性。
C++多态经典案例应用
1)游戏程序实例:游戏中的人物,道具,建筑物,每个对象都由自己的属性和方法,不同对象也可能有共同的属性和方法。从而,这部分共同的部分,可以设计为base
类,其他的可以在此基类上进行派生出来。
2)感觉微软的PPT好像也是这种模式,比如一个基类是基本的几何体性质,可以派生出一个矩形,还可以是正方形,在一个基类上进行override
操作即可。
3 基类的虚表放在内存什么位置?
首先,根据虚表的特征来进行推断:
1)虚函数表是类中全局共享元素,即全局仅有一个,在编译时就构造完成。
2)虚函数表是一个一位数组,里面的元素是指针指向类里面的虚函数,而类中虚函数的在编译时间就可以确定,即虚函数的大小是确定的,不需要动态内存分配,所以不在堆上;类对象中的前四个字节存在一个虚表指针,即vptr指向虚表。这里可以推出虚函数表不是函数,也即不是程序代码,不可能存储在text代码段。
3)虚函数表类似类中的静态成员变量,静态成员变量也是全局共享、大小确定,最优可能在全局数据区,在Linux/Unix下存放在可执行文件的只读数据段中(rodata),windows下在常量数据段。
4)虚表指针Vptr,对于有虚函数或者继承与拥有虚函数的类,对该类进行实例化时,在构造函数执行时会对虚表指针进行初始化,并且存在对象内存分布的最前面。
5)内存一般5区:栈区、自由区(不算)、堆区、全局数据区、常量区、代码段。参考C语言 08问题:内存五区 什么变量分别存储在什么区域,堆上还是栈上。
#晒一晒我的offer##软开##我发现了面试通关密码##面试#这是一个求职总结专栏,求职过程中,牛客里面各位同志,提供了很多面试的信息,对我个人有很大的帮助。这里简单将自己面试记录总结于此。 本人23届校招生,双非硕士,投递岗位嵌入式,控制算法,后台开发均有涉猎,优先级递减。简历累计投递数量:提前批(34)+正式批(128),累计Offer(5+)。