hello~ 很高兴见到大家! 这次带来的是C++中关于多态这部分的一些知识点,如果对你有所帮助的话,可否留下你宝贵的三连呢?
个 人 主 页: 默|笙
继上次的博客:多态(1)
一、虚函数表指针
- 拥有虚函数的类实例化出的对象里面都会存储至少一个虚函数表指针,一个类所有虚函数的地址都会存储到这个类对象的虚函数表中,虚函数表也简称虚表。
class Base
{
public:
virtual void Func1()
{
cout << "Func1()" << endl;
}
protected:
int _b = 1;
char _ch = 'x';
};
int main()
{
Base b;
cout << sizeof(b) << endl;
return 0;
}
- 它的输出结果在64位程序的运行结果是16,在这里面,虚函数表指针也就是_vfptr指针(v是virtual,f是function)占用了8个字节。
二、多态是如何实现的?
- 关于编译时多态:主要通过函数重载和模板实现,在编译阶段就能确定具体调用哪个函数的多态形式(静态绑定)。它的实现很大一部分取决于编译器会对重载的函数名进行修饰处理(会用一些特殊符号),便于查找。
- 关于运行时多态:主要通过虚函数重写实现,在编译阶段不能确定具体调用哪个函数的多态形式,需要在运行时通过传递一个对象来确定。它的实现取决于虚函数表的存在。
运行的时候,基类指针或引用指向谁就去这个对象的虚函数表里面找到对应的虚函数地址进行调用(动态绑定)。
三、动态绑定和静态绑定
- 对于不满足多态条件(基类指针或引用 + 调用虚函数)的函数调用是在编译时绑定,也就是编译时确定调用函数的地址,叫做静态绑定。
- 满足多态条件的函数调用是在运行的时候绑定,也就是在运行时到指定对象的虚函数表中找到调用函数的地址,叫做动态绑定。
反汇编:
四、虚函数表
- 基类对象的虚函数表中存放所有虚函数的地址。同类型的对象无论有多少个,都是共用同一张虚表,不同类型的对象各自有独立的虚表,所以基类和派生类有各自独立的虚表。
- 派生类会继承基类的虚函数表指针,不会再生成虚函数表指针。但是要注意这里继承下来的基类部分虚函数表指针和基类对象的虚函数表指针它不是同一个,就像基类对象的成员和派生类对象中的基类对象成员都是相互独立的。
- 派生类中重写的基类的虚函数,派生类的虚函数表中对应的虚函数会被覆盖成派生类重写的虚函数地址。所以重写也被叫做覆盖。
- 派生类的虚函数表中包含:1. 基类的虚函数地址,2. 派生类重写的虚函数完成覆盖,3. 派生类自己的虚函数地址三个部分。关于3,毕竟派生类也可以作为基类被继承。
- 派生类的对象可能不只有一个虚函数表,毕竟它可以继承多个基类,拥有多个虚函数表。在一些常见的编译器(如vs,g++)会把派生类独有的虚函数地址放在第一章虚函数表里面。这个c++标准没有规定。
- 虚函数表的本质其实就是一个函数指针数组,一般情况这个数组最后面放了一个0x00000000标记(vs下是这样,g++下没有)。
- 虚函数存在哪的?虚函数和普通函数一样,编译好后是一段指令,都是存在代码段的,只是虚函数的地址又存到了虚表中。
今天的分享就到此结束啦,如果对读者朋友们有所帮助的话,可否留下宝贵的三连呢~~
让我们共同努力, 一起走下去!