目录
前言
多态实现原理作为C++面试中的非常经典的面试题目, 我认为是非常值得我们拿出来聊一聊的~, 前一节我们聊了C++中强制类型转换是如何做的, 他只是C++语法层面的问题, 而这一节多态实现原理, 则是和C++的面向对象相关的部分, 很重要~
多态实现原理
静态多态
在C++中呢, 静态多态是基于函数重载来实现的~, 由编译器确定. 那函数重载是什么呢? 我们说: 在同一作用域下, 函数名相同, 函数参数列表不同(参数的个数, 参数的类型) 的几个函数相互成为函数重载, 这里需要注意的是, 函数的返回值是不能作为函数重载的判断条件的.
原理
本质是因为编译器在编译的时候(编译四步骤中: 编译阶段的语法分析, 会对符号进行符号汇总, 汇编阶段会生成函数名到函数地址的映射, 方便后面通过函数名找到函数地址), 会对每一个函数的函数名做函数名修饰, 修改为 特定字符 + 函数名 + 函数的参数列表的简称.
我们可以使用 objdump -t 可重定位目标文件.o / 可执行文件 来查看符号表, 我们可以看到被修饰之后的函数名如下: 1是类的名称的长度, A是类的名称, 3是函数名的长度, sum是函数名, 3Eii 和 Edd是函数的参数列表.

自定义类型的函数参数是如何做修饰的, 大家感兴趣可以用上面的方式去自己实验一下~~
动态多态
C++中, 动态多态 通过虚函数重写, 在运行时实现的, 虚函数重写是怎么做的呢? 基类的函数前面加上 virtual 修饰, 在派生类中重写这个函数, 在运行时会根据对象类型来调用相应的函数, 如果对象的类型是基类, 则调用基类的函数, 如果对象的类型是派生类, 则调用派生类的函数.
原理
动态多态的原理是利用了晚绑定技术, 和晚绑定相对的就是早绑定, 早绑定: 编译器编译时, 已经确定函数调用的地址.
晚绑定: 若类使用 virtual 修饰函数, 编译器则会为类生成虚函数表(一维数组, 存放了虚函数地址), 类对象构造时会初始化虚函数指针(指向虚表的指针) 在程序运行的过程中, 根据对象的类型再来确定要调用的函数.
对象的虚函数表是存放在这个类的最开始的4个或者8个字节的位置.

一个类, 会对应一个一个虚函数表, 由这个类实例化的对象, 会在自己的头4/8个字节保存虚函数表指针, 这个指针在这个类的构造函数中初始化(编译器自行完成).
虚函数表和虚函数指针的创建时机
虚函数表的创建时机
前面说了, 当一个class中的函数被virtual修饰之后, 编译器就会为这个为生成一个虚函数表, 虚函数表中的内容, 在编译期就已经确定了, 他的位置是在全局数据区的只读数据段中(.rodata段), 里面存放函数指针.
虚函数指针的创建时机
使用这个类实例化对象的时候, 在构造函数中, 将虚函数表的地址赋值给对象的vptr(对象最开始的4/8个字节), 如果类没有构造函数, 编译器会默认生成构造函数, 目的是帮我们为类对象初始化虚函数表指针.
继承下虚函数表指针的赋值过程, 在继承下, 是先构造父类, 再构造子类, 因此会现将父类的虚函数表赋值给子类的vptr, 再子类构造的时候, 重新为子类对象的vptr再一次赋值, 有两次赋值.
这里有一个要注意的是: C++中如果父类中的一个方法被virtual修饰, 子类中重写了这个方法, 即使这个方法前面没有加virtual, 编译器也会默认为这个方法加上virtual修饰.
小结
C++中的多态分为静态和动态, 分别的原理不同, 静态是函数重载, 动态是虚函数表和虚函数指针, 其中的细节上面已经阐述清楚啦~~
1262

被折叠的 条评论
为什么被折叠?



