本文会让看不见 摸不着的虚表(Vtable),虚指针(Vptr)彻底现行
本文涉及思想:
C++ 面向对象 封装 继承 多态 中的 多态
概念解释:
虚表指针:
这是指向虚表(vtable
)的指针,虚表中包含了该类的所有虚函数对应的地址。
虚表:
虚表是一个编译时生成的静态数组,存放在程序的数据段中,包含所有虚函数的地址(不属于类对象,与类对象无关)。每个类有自己的虚表,派生类可以继承基类的虚表并进行修改以反映任何虚函数的覆盖(重写)。
sizeof的严格语义:
A a;
sizeof(A)
和 sizeof(a)
都表示类对象的大小,但它们从语法上指向稍有不同的含义:
sizeof(A)
:这表示测量 A
类型的对象所占用的内存大小,即类 A
的实例化对象的大小。这是从类型层面进行的测量,表明创建一个该类型的实例所需的字节数。
sizeof(a)
:这表示测量已经实例化的对象 a
的大小,其中 a
是 A
类的一个实例。这实际上将给出与 sizeof(A)
相同的结果,因为 a
是 A
类型。
如果一个类是空的,那么它占1字节,为了防止类对象重叠.
例如新建10个类,如果大小是0,内存中无论是顺序还是随机,都有概率会使用相同的内存地址
#include <iostream>
using namespace std;
class A
{
public:
};
int main()
{
cout << sizeof(A) << endl; // 1
return 0;
}
放入一个普通函数,不会改变类对象大小:
类对象的行为属于类的共享资源,存储在代码段中
#include <iostream>
using namespace std;
class A
{
public:
void show(){};//虚函数和普通函数都不占类对象大小
};
int main()
{
cout << sizeof(A) << endl; // 1
return 0;
}
放入一个虚函数,则编译器会为该类创建一个虚指针,64位机器为8字节
#include <iostream>
using namespace std;
class A
{
public:
virtual void show(){}; // 虚函数和普通函数都不占类对象的大小
};
int main()
{
cout << sizeof(A) << endl; // 64位系统 Vptr占8字节
return 0;
}
为了验证上述猜想:
#include <iostream>
using namespace std;
class A
{
public:
virtual void show1(){};
virtual void show2(){};
virtual void show3(){};
virtual void show4(){};
};
int main()
{
cout << sizeof(A) << endl; // 64位系统 Vptr占8字节
return 0;
}
无论放几个虚函数,始终只存在一个虚指针
借上面例子,这个虚指针指向一个虚表:
这个虚表就是一个函数指针数组 存放了show1,show2,show3,show4的地址,也是C++实现多态的核心机制
下面我们要在GDB中观察到这4个地址:
g++ -g -o c2 c1.cpp
gdb c2
(gdb) break main
(gdb) run
(gdb) info vtbl b
你写了几个虚函数看见几个地址就行,地址转回函数名的验证:
(gdb) info symbol 地址
无法实现,可能是内存优化导致