虚函数表结构

article/2025/10/4 3:20:24

虚函数表

所谓虚函数表就是存放着当前类中所有虚函数地址的表。在实例化一个具有虚函数的类时,这个表也被分配到这个实例对象的内存中,通过虚函数表可以指明所要调用的函数的位置。在C++编译器中虚函数表的地址存放在对象的最前面,这是为了即使多继承下也能快速获取到虚函数表。

我们可以通过下示的代码简单看下虚函数表结构:

class Base{
public:virtual void A(){cout << "Base::A()" << endl;}virtual void B(){cout << "Base::B()" << endl;}void C(){cout << "Base::C()" << endl;}int D;
};int main(){Base* b1 = new Base;return 0;
}

这里有个问题:为什么b1中没有成员函数 C 的信息?

因为成员函数在编译期间确定,对象在调用时直接call函数名去调用,所以不需要在对象中存储成员函数信息。

单继承下的虚函数表

在了解了虚函数表在单个实例化对象中的结构后,我们看一下在单继承下的虚函数表。

  • 未覆盖

这里所提到的覆盖也就是常说的重写是指:派生类在继承父类虚函数之后有没有重载基类的函数,如果有就会发生覆盖,反之。

同样,我们运行下述代码进行实验:

class Base{
public:virtual void A(){ cout << "Base::A()" << endl; }virtual void B(){ cout << "Base::B()" << endl; }
};class Derive :public Base{
public:virtual void C(){ cout << "Derive::C()" << endl; }virtual void D(){ cout << "Derive::D()" << endl; }
};int main(){Base b1;Derive d1;return 0;
}

但结果并不是我们所想的那样:

这里面没有我们派生类的虚函数指针。这是因为编译器故意隐藏了这两个函数。

不过我们可以在通过代码打印出来。

思路:取出b1、d1对象的头4bytes,就是虚表的指针,而虚函数表本质是一个存虚函数指针的指针数组,这个数组最后面放了一个nullptr。
     1.先取b1的地址,强转成一个int*的指针;
     2.再解引用取值,就取到了b1对象头4bytes的值,这个值就是指向虚表的指针;
     3.再强转成pVtable*,因为虚表就是一个存虚函数指针类型的数组;
     4.虚表指针传递给PrintVTable进行打印虚表;
     5.需要说明的是这个打印虚表的代码经常会崩溃,因为编译器有时对虚表的处理不干净,虚表最后面没有放nullptr,导致越界,这是编译器的问题。我们只需要点目录栏的-生成-清理解决方案,再编译就好了。

代码实现:

typedef void(*pVtable)(void);void PrintVTable(pVtable vTable[])
{// 依次取虚表中的虚函数指针打印并调用。调用就可以看出存的是哪个函数cout << " 虚表地址>" << vTable << endl;for (int i = 0; vTable[i] != nullptr; ++i){printf(" 第%d个虚函数地址 :0X%x,->", i, vTable[i]);pVtable f = vTable[i];f();}cout << endl;
}int main(){Base b1;Derive d1;pVtable* vTableb = (pVtable*)(*(int*)&b1);PrintVTable(vTableb);pVtable* vTabled = (pVtable*)(*(int*)&d1);PrintVTable(vTabled);return 0;
}

执行结果:

如果发生如下图所示错误,中断-->生成-->清理解决方案-->重新生成解决方案   就OK了:

结论:

  1. 虚函数按照其声明顺序放于表中。
  2. 父类的虚函数在子类的虚函数前面。
  • 覆盖

一般来讲基类声明虚函数就是为了让派生类重写,所以我们了解下重写之后的虚函数表。

只需在派生类重写下基类的方法:

class Derive :public Base{
public:void A(){ cout << "Derive::A()" << endl; }virtual void C(){ cout << "Derive::C()" << endl; }virtual void D(){ cout << "Derive::D()" << endl; }
};

执行完结果,明显看出此时派生类对象中派生类虚函数地址已将基类虚函数地址覆盖掉了,而没有重写的虚函数B依然与基类地址相同:

结论:

如果派生类对基类的虚函数进行重写,则将被重写的虚函数地址覆盖为派生类的函数地址。其他未被覆盖的保持不变。

 

多继承下的虚函数表:

看完单继承自然而然开始讨论多继承下的虚函数表,同样分为两种情况讨论:

  • 未覆盖

在之前代码中添加Base2类,并让Derive继承Base2,创建Base2对象b2。

class Base{
public:virtual void A(){ cout << "Base::A()" << endl; }virtual void B(){ cout << "Base::B()" << endl; }
};
class Base2{virtual void A(){ cout << "Base::A()" << endl; }virtual void B(){ cout << "Base::B()" << endl; }
};
class Derive :public Base, public Base2{
public:virtual void C(){ cout << "Derive::C()" << endl; }virtual void D(){ cout << "Derive::D()" << endl; }
};

由于多继承下派生类会产生多个虚表,就本例而言会有两个虚表如下图所示:

所以我们要取两次虚表地址:

	pVtable* vTabled = (pVtable*)(*(int*)&d1);PrintVTable(vTabled);vTabled = (pVtable*)(*(int*)((char* )&d1+sizeof(Base)));PrintVTable(vTabled);

执行结果:

如果我们将继承顺序颠倒:

class Derive :public Base2, public Base{
public:virtual void C(){ cout << "Derive::C()" << endl; }virtual void D(){ cout << "Derive::D()" << endl; }
};

执行结果:

结论:

  1. 每个基类都要一张虚函数表,且在派生类中按序排放(这里的按序指声明顺序);
  2. 派生类自己的虚函数地址存放在第一张虚函数表中。(这样做就是为了解决不同的父类类型的指针指向同一个子类实例,而能够调用到实际的函数。)
  • 覆盖

最后在看看多继承下覆盖后的虚函数表,结合之前示例实现此过程就很容易,不再贴代码直接运行出结果:

结论:

基类虚函数A()位置被替换成了派生类的函数指针。这样,我们就可以任一静态类型的基类来指向派生类,并调用派生类的A()了。

 


http://chatgpt.dhexx.cn/article/7JPQIgRq.shtml

相关文章

关于虚函数与虚函数表

首先&#xff0c;我们知道&#xff0c;C的动态多态是基于虚函数实现的 。 C能够在运行时确定调用的函数是因为引入了虚函数&#xff0c;在类中引入虚函数后,在程序编译期间就会创建虚函数表&#xff0c;表中每一项数据都是虚函数的入口地址。 然而&#xff0c;怎么才能访问到虚…

C++中的虚函数表

引言&#xff1a; 多态对于C这种面向对象的语言来讲&#xff0c;其重要性是不言而喻的&#xff0c;用了足足半天的时间来把我所理解的多态表达出来&#xff0c;其中还有很多细节需要以后补充。&#xff08;一个字一个字写&#xff0c;还要画图&#xff0c;太累了&#xff09; …

虚函数原理与虚函数表

目录 一、 虚函数 二、虚函数原理与虚函数表 一、 虚函数 虚函数&#xff1a; 使用 virtual 关键字声明的函数&#xff0c;是动态多态实现的基础。 非类的成员函数不能定义为虚函数。 类的静态成员函数不能定义为虚函数。 构造函数不能定义为虚函数&#xff0c;但可以将析构函…

c++虚函数和虚函数表

什么是虚函数? 用virtual 修饰的成员函数叫虚函数 没有虚构造函数 不写虚函数&#xff0c;没有默认的虚函数 虚函数对于类的影响&#xff1a;增加一个指针的内存 虚函数的存储&#xff1a;虚函数表(了解内容&#xff1a;就是一个指针存储所有虚函数的首地址[函数指…

虚函数表存储位置

前言 先说结论&#xff1a;虚函数表存储在只读数据段&#xff08;.rodata&#xff09;、虚函数存储在代码段&#xff08;.text&#xff09;、虚表指针的存储的位置与对象存储的位置相同&#xff0c;可能在栈、也可能在堆或数据段等。 相信大家知道虚表指针和虚函数存储的位置…

C++虚函数表详解

C的虚函数(Virtual Function)是通过一张虚函数表(Virtual Table)来实现的。简称为V-Table。在这个表中&#xff0c;主要是一个类的虚函数的地址表&#xff0c;这张表解决了继承、覆盖(override)的问题&#xff0c;保证其能真实的反应实际的函数。这样&#xff0c;在有虚函数的类…

【虚函数指针 虚函数表】

文章目录 虚函数指针和虚函数表1.虚函数的含义2.虚函数的作用3.虚函数的实现原理 多态的实现原理普通类当类中存在虚函数子类继承父类不重写虚函数子类继承父类重写虚函数 1.虚函数表指针2.虚函数表 虚函数指针和虚函数表 1.虚函数的含义 只有用virtual声明类的成员函数&…

虚函数表 以及 虚函数表的继承过程

目录 一、虚函数表 和 虚表继承 1、虚函数表 2、虚表继承 (1) 子类未重写父类虚函数 (2) 子类重写了父类虚函数 二、虚表的特点 1、同一个类的对象的虚表指针相同 2、多继承时子类中的两个父类虚表地址不一样&#xff08;实际调用是同一个函数&#xff09; 三、虚表指…

C++虚函数和虚函数表原理

虚函数的地址存放于虚函数表之中。运行期多态就是通过虚函数和虚函数表实现的。 类的对象内部会有指向类内部的虚表地址的指针。通过这个指针调用虚函数。 虚函数的调用会被编译器转换为对虚函数表的访问&#xff1a; ptr->f(); //ptr代表this指针&#xff0c;f是虚函数…

虚函数 虚函数表

虚函数是面向对象编程函数的一种特定形态&#xff0c;是C用于实现多态的一种有效机制。C的多态可以分为静态多态和动态多态。函数重载和运算符重载实现的多态属于静态多态&#xff0c;而通过虚函数可以实现动态多态。实现函数的动态联编其本质核心则是虚表指针与虚函数表。 虚…

虚函数表和虚表指针

1&#xff0c;虚函数的含义 用virtual声明类的成员函数称之为虚函数 2&#xff0c;作用 用于实现多态 存在继承关系&#xff0c;子类继承父类子类重写了父类的virtual function子类以父类的指针或者引用的身份出现 3&#xff0c;虚函数的实现原理 其中的关键就是两点&#xf…

C++ 虚函数和虚函数表

一、虚函数 1.虚函数的概念 1.虚函数就是在基类中被关键字 virtual 说明&#xff0c;并在派生类中重新定义的函数。 2.虚函数的作用是允许在派生类中重新定义与基类同名的函数&#xff0c;并且可以通过基类指针或引用来访问基类和派生类中的同名函数。 3.虚函数的定义是在基类…

C++虚函数表剖析

关键词&#xff1a;虚函数&#xff0c;虚表&#xff0c;虚表指针&#xff0c;动态绑定&#xff0c;多态 一、概述 为了实现 C 的多态&#xff0c;C 使用了一种动态绑定的技术。这个技术的核心是虚函数表&#xff08;下文简称虚表&#xff09;。本文介绍虚函数表是如何实现动态…

C++ 虚函数表解析

C 虚函数表解析 陈皓 http://blog.csdn.net/haoel 前言 C中的虚函数的作用主要是实现了多态的机制。关于多态&#xff0c;简而言之就是用父类型别的指针指向其子类的实例&#xff0c;然后通过父类的指针调用实际子类的成员函数。这种技术可以让父类的指针有“多种形态”&#x…

虚函数表详解

关键词&#xff1a;虚函数&#xff0c;虚表&#xff0c;虚表指针&#xff0c;动态绑定&#xff0c;多态 一、概述 为了实现C的多态&#xff0c;C使用了一种动态绑定的技术。这个技术的核心是虚函数表&#xff08;下文简称虚表&#xff09;。本文介绍虚函数表是如何实现动态绑定…

虚函数及虚函数表

虚函数及虚函数表 各个类对象共享类的虚函数表&#xff0c;每个类对象有个虚函数指针vptr&#xff0c;虚函数指针vptr指向虚函数表&#xff08;对于只有一个虚函数表的情况&#xff09;。 虚函数 简单的说&#xff0c;每一个含有虚函数&#xff08;无论是其本身的&#xff0…

简述虚函数表

前段时间我在博客中简单地说了下C的虚函数&#xff0c;所谓虚函数&#xff0c;就是C实现多态性的方法。那么编译器是如何识别虚函数的呢&#xff1f;据百度百科描述&#xff0c;C并未规定用何种方法实现虚函数&#xff0c;但是大部分编译器厂商都选择使用虚函数表这种方法&…

虚函数表的问题

虚函数表&#xff1a; 多态是由虚函数实现的&#xff0c;而虚函数主要是通过虚函数表&#xff08;V-Table&#xff09;来实现的。 如果一个类中包含虚函数&#xff08;virtual修饰的函数&#xff09;&#xff0c;那么这个类就会包含一张虚函数表&#xff0c;虚函数表存储的每…

虚函数表详解及其应用场景

目录 概述1. 虚函数表概述2. 虚函数表的实现原理2.1. 虚函数的声明和定义2.2. 虚函数表的创建和初始化2.3. 虚函数调用的过程 3. 虚函数表的应用场景3.1. 多态性3.2. 基类指针和引用的使用3.3. 动态绑定3.4. 接口定义 结论 概述 在面向对象编程中&#xff0c;虚函数表&#xf…

C++ 面试必问:深入理解虚函数表

点击蓝字 关注我们 深入理解C 虚函数表 C中的虚函数的作用主要是实现了多态的机制。关于多态&#xff0c;简而言之就是用父类型别的指针指向其子类的实例&#xff0c;然后通过父类的指针调用实际子类的成员函数。 Derive d; Base1 *b1 &d; Base2 *b2 &d; Base3 *b3 …