文章目录
- 一、验证指向 虚函数表 的 vptr 指针 是否存在
- 1、虚函数表与 vptr 指针由来
- 2、虚函数类与普通函数类对比 - 多出了 vptr 指针的大小
对比 定义了 虚函数 的类 与 没有定义虚函数的类 的大小 , 其它成员都相同 , 定义了虚函数的类多出了 4 字节 , 多出的 4 字节就是 vptr 指针占用的内存空间 ;
一、验证指向 虚函数表 的 vptr 指针 是否存在
1、虚函数表与 vptr 指针由来
" 虚函数表 " 由 C++ 编译器 负责 创建 与 维护 , 被 virtual 关键字 修饰的 虚函数 , 会自动 被 C++ 编译器 存储到 " 虚函数表 " 中 ;
虚函数表 创建 : 在 类 中使用 virtual 关键字 声明 虚函数 时 , C++ 编译器 会自动为该类生成 " 虚函数表 " ;
- 生成虚函数表的前提是 至少有 1 个虚函数 ;
- 如果 没有虚函数 , 就不会生成虚函数表 ;
- 如果 类 中有 virtual 虚函数 , 则 该类的 每个对象 中 , 都有一个 指向 虚函数表的 vptr 指针 ;
虚函数表 存储 虚函数指针 : " 虚函数表 " 是 存储 " 类成员函数指针 " 的 数据结构 , 是一个 函数指针数组 , 数组中的元素都是函数指针 , 具体存储的都是 指向 类中的虚函数 的指针 ;
- 如果 子类 中 , 重写了 父类的 virtual 虚函数 , 那么 C++ 编译器会在 子类 虚函数表 中放入该 子类虚函数的 函数指针 ;
如果 C++ 类中存在 virtual 虚函数 , 在创建对象时 , 会生成 虚函数表 Virtual Function Table , 简称 vtable ;
C++ 编译器 编译 代码时 , 会自动为该类 添加 一个 vptr 指针 成员变量 , 该指针 会指向 虚函数表 ;
2、虚函数类与普通函数类对比 - 多出了 vptr 指针的大小
下面的代码中 , 定义了 2 个类 , 区别是 一个定义了 virtual 虚函数 , 另外一个没有定义 虚函数 ;
- 在 Parent 中定义了 虚函数 virtual void fun(int a) ;
- 在 Parent2 中定义的是 普通函数 void fun(int a) ;
使用 sizeof 函数 , 获取这两个类的大小 , 判断两个类的区别 ;
最终得到 , 有 虚函数 的 类 , 比 没有 虚函数 的 类 , 多 4 字节 , 也就是一个指针的大小 ,
定义了 虚函数 的类 , 多出的 4 字节就是 vptr 指针的大小 ;
代码示例 :
#include "iostream"
using namespace std;
// 父类
class Parent {
public:
virtual void fun(int a)
{
cout << "执行 父类 Parent 的 virtual void fun(int a) 函数" << endl;
}
int a;
};
// 父类
class Parent2 {
public:
void fun(int a)
{
cout << "执行 父类 Parent2 的 void fun(int a) 函数" << endl;
}
int a;
};
// 子类
class Child : public Parent {
public:
virtual void fun(int a)
{
cout << "执行 子类 virtual void fun(int a) 函数" << endl;
}
};
int main() {
Parent* p;
// 创建 Child 子类对象时
// 发现有 virtual 虚函数 会创建 虚函数表
// 在对象中使用 vptr 指针指向 虚函数表 首地址
Child c;
// 将父类指针指向子类对象
p = &c;
// 通过父类指针调用子类对象的 fun 函数
p->fun(1);
// 打印 Parent 的 大小
cout << "sizeof(Parent) : " << sizeof(Parent) << endl;
// 打印 Parent2 的 大小
cout << "sizeof(Parent2) : " << sizeof(Parent2) << endl;
// 控制台暂停 , 按任意键继续向后执行
system("pause");
return 0;
}
执行结果 :
执行 子类 virtual void fun(int a) 函数
sizeof(Parent) : 8
sizeof(Parent2) : 4
Press any key to continue . . .