C++虚函数表深度剖析

多态是由虚函数实现的,而虚函数主要是通过**虚函数表(V-Table)**来实现的。

目录

引入虚函数表

虚函数表图解

派生类覆盖基类函数

派生类不覆盖基类函数

多继承下的虚函数


引入虚函数表

看下面的代码

cpp 复制代码
#include <iostream>

using namespace std;

class Base {
public:
	virtual void f() { cout << "f()" << endl; }
	virtual void g() { cout << "g()" << endl; }
	virtual void h() { cout << "h()" << endl; }
    int a;
};

int main()
{
	Base b;
	(((void(*)())*((size_t*)(*((size_t*)&b)) + 0)))();
	(((void(*)())*((size_t*)(*((size_t*)&b)) + 1)))();
	(((void(*)())*((size_t*)(*((size_t*)&b)) + 2)))();
	return 0;
}

虚函数表图解

上面的代码,咱们用一张图来表示就是下面这样

只要一个类中有虚函数,那这个类中就一定会有一张虚函数表,虚函数表中的每一项都是虚函数的地址。

这个类的每一个对象都会包含一个虚指针(虚指针存在于对象实例地址的最前面,保证虚函数表有最高的性能),这个虚指针指向虚函数表。

虚指针是对象中的一个隐藏成员(在大多数实现中),它指向该类(或其最近的一个包含虚函数的基类)的虚函数表。

需要注意的是,在C++中,对象本身通常不包含完整的虚函数表。对象包含的是类的成员变量和可能的虚指针(也称为vptr,即指向虚函数表的指针)。

虚函数表是与类相关联的,而不是与特定的对象相关联。每个包含虚函数的类(或其派生类,如果它们重写了虚函数或添加了新的虚函数)都会有一个虚函数表。

派生类覆盖基类函数

cpp 复制代码
#include <iostream>

using namespace std;

class Base {
public:
	virtual void f() { cout << "f()" << endl; }
	virtual void g() { cout << "g()" << endl; }
	virtual void h() { cout << "h()" << endl; }
};
class child : public Base
{
    virtual void f() {std::cout<<"x()"<<std::endl;}
};
int main()
{
	child t;
	(((void(*)())*((size_t*)(*((size_t*)&b)) + 0)))();
	(((void(*)())*((size_t*)(*((size_t*)&b)) + 1)))();
	(((void(*)())*((size_t*)(*((size_t*)&b)) + 2)))();
	return 0;
}

派生类覆盖基类函数之后,虚表中派生类覆盖的虚函数的地址被放在了基类相应的函数原来的位置 ,没有覆盖的则不变。

派生类不覆盖基类函数

cpp 复制代码
#include <iostream>

using namespace std;

class Base {
public:
	virtual void f() { cout << "f()" << endl; }
	virtual void g() { cout << "g()" << endl; }
	virtual void h() { cout << "h()" << endl; }
};
class child : public Base
{
    virtual void j() {std::cout<<"j()"<<std::endl;}
    virtual void k() {std::cout<<"k()"<<std::endl;}
    virtual void l() {std::cout<<"l()"<<std::endl;}
};
int main()
{
	child t;
	(((void(*)())*((size_t*)(*((size_t*)&t)) + 0)))();
	(((void(*)())*((size_t*)(*((size_t*)&t)) + 1)))();
	(((void(*)())*((size_t*)(*((size_t*)&t)) + 2)))();
    (((void(*)())*((size_t*)(*((size_t*)&t)) + 3)))();
	(((void(*)())*((size_t*)(*((size_t*)&t)) + 4)))();
	(((void(*)())*((size_t*)(*((size_t*)&t)) + 5)))();
	return 0;
}

Child class继承了 Base class 中的三个虚函数,准确的说,是该函数实体的地址被拷贝到 Derive类的虚函数表,派生类新增的虚函数置于虚函数表的后面,并按声明顺序存放

多继承下的虚函数表

cpp 复制代码
#include <iostream>

using namespace std;

class Base {
public:
	virtual void f() { cout << "f()" << endl; }
	virtual void g() { cout << "g()" << endl; }
	virtual void h() { cout << "h()" << endl; }
};
class Base2 {
public:
	virtual void a() { cout << "a()" << endl; }
	virtual void b() { cout << "b()" << endl; }
	virtual void c() { cout << "c()" << endl; }
};
class child : public Base,public Base2
{
    virtual void j() {std::cout<<"j()"<<std::endl;}
    virtual void k() {std::cout<<"k()"<<std::endl;}
    virtual void l() {std::cout<<"l()"<<std::endl;}
};
int main()
{
	child base1_vptr;
	(((void(*)())*((size_t*)(*((size_t*)&base1_vptr)) + 0)))();
	(((void(*)())*((size_t*)(*((size_t*)&base1_vptr)) + 1)))();
	(((void(*)())*((size_t*)(*((size_t*)&base1_vptr)) + 2)))();
    (((void(*)())*((size_t*)(*((size_t*)&base1_vptr)) + 3)))();
	(((void(*)())*((size_t*)(*((size_t*)&base1_vptr)) + 4)))();
	(((void(*)())*((size_t*)(*((size_t*)&base1_vptr)) + 5)))();
    size_t *base2_vptr=(size_t*)*(((size_t*)&base1_vptr)+1);
    (((void(*)())*((size_t*)(*((size_t*)&base2_vptr)) + 0)))();
	(((void(*)())*((size_t*)(*((size_t*)&base2_vptr)) + 1)))();
	(((void(*)())*((size_t*)(*((size_t*)&base2_vptr)) + 2)))();
	return 0;
}

这个派生类多重继承了两个基类base1,base2,因此它有两个虚函数表。它的对象会有多个虚指针(据说和编译器相关),指向不同的虚函数表。

相关推荐
重启的码农10 分钟前
Windows虚拟显示器MttVDD源码分析 (6) 高级色彩与HDR管理
c++·windows·操作系统
花开富贵ii14 分钟前
代码随想录算法训练营四十九天|图论part07
java·数据结构·算法·图论·prim·kruscal
東雪蓮☆18 分钟前
深入理解 iptables:Linux 防火墙从入门到精通
linux·运维·网络
jingfeng51420 分钟前
C++多态
开发语言·c++
kyle~33 分钟前
C/C++---浮点数与整形的转换,为什么使用sqrt函数时,要给参数加上一个极小的小数(如1e-6)
c语言·开发语言·c++
CoovallyAIHub33 分钟前
无需ReID网络!FastTracker凭借几何与场景认知实现多目标跟踪新SOTA,助力智慧交通更轻更快
深度学习·算法·计算机视觉
jokr_34 分钟前
C++ STL 专家容器:关联式、哈希与适配器
java·c++·哈希算法
努力学习的小廉38 分钟前
深入了解linux系统—— 线程互斥
linux·运维·服务器
CoovallyAIHub1 小时前
D‘RespNeT无人机图像分割数据集与YOLOv8-DRN模型,实时识别入口与障碍,助力灾后救援
深度学习·算法·计算机视觉
暖苏1 小时前
python-多线程(笔记)(持续更新)
大数据·开发语言·python