C++虚函数表

article/2025/10/4 2:43:10

一、背景知识(一些基本概念)

虚函数(Virtual Function):在基类中声明为 virtual 并在一个或多个派生类中被重新定义的成员函数。
纯虚函数(Pure Virtual Function):基类中没有实现体的虚函数称为纯虚函数(有纯虚函数的基类称为虚基类)。
C++  “虚函数”的存在是为了实现面向对象中的“多态”,即父类类别的指针(或者引用)指向其子类的实例,然后通过父类的指针(或者引用)调用实际子类的成员函数。通过动态赋值,实现调用不同的子类的成员函数(动态绑定)。正是因为这种机制,把析构函数声明为“虚函数”可以防止在内存泄露。
实例:

复制代码

#include <iostream>
using namespace std;class base_class
{
public:base_class(){}virtual ~base_class(){}int normal_func(){cout << "This is  base_class's normal_func()" << endl;return 0;}virtual int virtual_fuc(){cout << "This is  base_class's virtual_fuc()" << endl;return 0;}};class drived_class1 : public base_class
{
public:drived_class1(){}virtual ~drived_class1(){}int normal_func(){cout << "This is  drived_class1's normal_func()" << endl;return 0;}virtual int virtual_fuc(){cout << "This is  drived_class1's virtual_fuc()" << endl;return 0;}
};class drived_class2 : public base_class
{
public:drived_class2(){}virtual ~drived_class2(){}int normal_func(){cout << "This is  drived_class2's normal_func()" << endl;return 0;}virtual int virtual_fuc(){cout << "This is  drived_class2's virtual_fuc()" << endl;return 0;}
};int main()
{base_class * pbc = NULL;base_class bc;drived_class1 dc1;drived_class2 dc2;pbc = &bc;pbc->normal_func();pbc->virtual_fuc();pbc = &dc1;pbc->normal_func();pbc->virtual_fuc();pbc = &dc2;pbc->normal_func();pbc->virtual_fuc();return 0;
}

复制代码

输出结果:

复制代码

This is  base_class's normal_func()
This is  base_class's virtual_fuc()
This is  base_class's normal_func()
This is  drived_class1's virtual_fuc()
This is  base_class's normal_func()
This is  drived_class2's virtual_fuc()

复制代码

 

假如将 base_class 类中的 virtual_fuc() 写成下面这样(纯虚函数,虚基类):

// 无实现体
virtual int virtual_fuc() = 0;

那么 virtual_fuc() 是一个纯虚函数,base_class 就是一个虚基类:不能实例化(就是不能用它来定义对象),只能声明指针或者引用。读者可以自行测试,这里不再给出实例。


虚函数表(Virtual Table,V-Table):使用 V-Table 实现 C++ 的多态。在这个表中,主要是一个类的虚函数的地址表,这张表解决了继承、覆盖的问题,保证其真实反应实际的函数。这样,在有虚函数的类的实例中分配了指向这个表的指针的内存,所以,当用父类的指针来操作一个子类的时候,这张虚函数表就显得尤为重要了,它就像一个地图一样,指明了实际所应该调用的函数。
编译器应该保证虚函数表的指针存在于对象实例中最前面的位置(这是为了保证取到虚函数表的有最高的性能——如果有多层继承或是多重继承的情况下)。 这意味着可以通过对象实例的地址得到这张虚函数表,然后就可以遍历其中函数指针,并调用相应的函数。

二、无继承时的虚函数表

复制代码

#include <iostream>
using namespace std;class base_class
{
public:virtual void v_func1(){cout << "This is base_class's v_func1()" << endl;}virtual void v_func2(){cout << "This is base_class's v_func2()" << endl;}virtual void v_func3(){cout << "This is base_class's v_func3()" << endl;}
};int main()
{// 查看 base_class 的虚函数表base_class bc;cout << "base_class 的虚函数表首地址为:" << (int*)&bc << endl; // 虚函数表地址存在对象的前四个字节cout << "base_class 的 第一个函数首地址:" << (int*)*(int*)&bc+0 << endl; // 指针运算看不懂?没关系,一会解释给你听cout << "base_class 的 第二个函数首地址:" << (int*)*(int*)&bc+1 << endl;cout << "base_class 的 第三个函数首地址:" << (int*)*(int*)&bc+2 << endl;cout << "base_class 的 结束标志: " << *((int*)*(int*)&bc+3) << endl;// 通过函数指针调用函数,验证正确性typedef void(*func_pointer)(void);func_pointer fp = NULL;fp = (func_pointer)*((int*)*(int*)&bc+0); // v_func1()fp();fp = (func_pointer)*((int*)*(int*)&bc+1); // v_func2()fp();fp = (func_pointer)*((int*)*(int*)&bc+2); // v_func3()fp();return 0;
}

复制代码

输出结果:

复制代码

base_class 的虚函数表首地址为:0x22ff0c
base_class 的 第一个函数首地址:0x472c98
base_class 的 第二个函数首地址:0x472c9c
base_class 的 第三个函数首地址:0x472ca0
base_class 的虚函数表结束标志: 0
This is base_class's v_func1()
This is base_class's v_func2()
This is base_class's v_func3()

复制代码


简单的解释一下代码中的指针转换:
&bc:获得 bc 对象的地址
(int*)&bc: 类型转换,获得虚函数表的首地址。这里使用 int* 的原因是函数指针的大小的 4byte,使用 int* 可以使得他们每次的偏移量保持一致(sizeof(int*) = 4,32-bit机器)。
*(int*)&bc:解指针引用,获得虚函数表。
(int*)*(int*)&bc+0:和上面相同的类型转换,获得虚函数表的第一个虚函数地址。
(int*)*(int*)&bc+1:同上,获得第二个函数地址。
(int*)*(int*)&bc+2:同上,获得第三个函数地址。
*((int*)*(int*)&bc+3:获得虚函数表的结束标志,所以这里我解引用了。和我们使用链表的情况是一样的,虚函数表当然也需要一个结束标志。
typedef void(*func_pointer)(void):定义一个函数指针,参数和返回值都是 void。
*((int*)*(int*)&bc+0):找到第一个函数,注意这里需要解引用。
对于指针的转换,我就解释这么多了。下面的文章,我不再做解释,相信大家可以举一反三。如果你觉得很费解的话,我不建议继续去看这篇文章了,建议你去补一补基础(《C和指针》是一本很好的选择哦!)。

通过上面的例子的尝试和输出结果,我们可以得出下面的布局图示:

三、单一继承下的虚函数表
3.1子类没有父类的虚函数(陈皓文章中用了“覆盖”一词,我觉得太合理,但是我又找不到更合理的词语,所以就用一个句子代替了。^-^)

复制代码

#include <iostream>
using namespace std;class base_class
{
public:virtual void v_func1(){cout << "This is base_class's v_func1()" << endl;}virtual void v_func2(){cout << "This is base_class's v_func2()" << endl;}virtual void v_func3(){cout << "This is base_class's v_func3()" << endl;}
};
class dev_class : public base_class
{
public:virtual void v_func4(){cout << "This is dev_class's v_func4()" << endl;}virtual void v_func5(){cout << "This is dev_class's v_func5()" << endl;}
};int main()
{// 查看 dev_class 的虚函数表dev_class dc;cout << "dev_class 的虚函数表首地址为:" << (int*)&dc << endl;cout << "dev_class 的 第一个函数首地址:" << (int*)*(int*)&dc+0 << endl;cout << "dev_class 的 第二个函数首地址:" << (int*)*(int*)&dc+1 << endl;cout << "dev_class 的 第三个函数首地址:" << (int*)*(int*)&dc+2 << endl;cout << "dev_class 的 第四个函数首地址:" << (int*)*(int*)&dc+3 << endl;cout << "dev_class 的 第五个函数首地址:" << (int*)*(int*)&dc+4 << endl;cout << "dev_class 的虚函数表结束标志: " << *((int*)*(int*)&dc+5) << endl;// 通过函数指针调用函数,验证正确性typedef void(*func_pointer)(void);func_pointer fp = NULL;for (int i=0; i<5; i++) {fp = (func_pointer)*((int*)*(int*)&dc+i);fp();}return 0;
}

复制代码

 输出结果:

复制代码

dev_class 的虚函数表首地址为:0x22ff0c
dev_class 的 第一个函数首地址:0x472d10
dev_class 的 第二个函数首地址:0x472d14
dev_class 的 第三个函数首地址:0x472d18
dev_class 的 第四个函数首地址:0x472d1c
dev_class 的 第五个函数首地址:0x472d20
dev_class 的虚函数表结束标志: 0
This is base_class's v_func1()
This is base_class's v_func2()
This is base_class's v_func3()
This is dev_class's v_func4()
This is dev_class's v_func5()

复制代码

通过上面的例子的尝试和输出结果,我们可以得出下面的布局图示:


可以看出,v-table中虚函数是顺序存放的,先基类后派生类。

3.2子类有重写父类的虚函数

复制代码

include <iostream>
using namespace std;class base_class
{
public:virtual void v_func1(){cout << "This is base_class's v_func1()" << endl;}virtual void v_func2(){cout << "This is base_class's v_func2()" << endl;}virtual void v_func3(){cout << "This is base_class's v_func3()" << endl;}
};
class dev_class : public base_class
{
public:virtual void v_func1(){cout << "This is dev_class's v_func1()" << endl;}virtual void v_func2(){cout << "This is dev_class's v_func2()" << endl;}virtual void v_func4(){cout << "This is dev_class's v_func4()" << endl;}virtual void v_func5(){cout << "This is dev_class's v_func5()" << endl;}
};int main()
{// 查看 dev_class 的虚函数表dev_class dc;cout << "dev_class 的虚函数表首地址为:" << (int*)&dc << endl;cout << "dev_class 的 第一个函数首地址:" << (int*)*(int*)&dc+0 << endl;cout << "dev_class 的 第二个函数首地址:" << (int*)*(int*)&dc+1 << endl;cout << "dev_class 的 第三个函数首地址:" << (int*)*(int*)&dc+2 << endl;cout << "dev_class 的 第四个函数首地址:" << (int*)*(int*)&dc+3 << endl;cout << "dev_class 的 第五个函数首地址:" << (int*)*(int*)&dc+4 << endl;cout << "dev_class 的虚函数表结束标志: " << *((int*)*(int*)&dc+5) << endl;// 通过函数指针调用函数,验证正确性typedef void(*func_pointer)(void);func_pointer fp = NULL;for (int i=0; i<5; i++) {fp = (func_pointer)*((int*)*(int*)&dc+i);fp();}return 0;
}

复制代码

输出结果:

复制代码

dev_class 的虚函数表首地址为:0x22ff0c
dev_class 的 第一个函数首地址:0x472d50
dev_class 的 第二个函数首地址:0x472d54
dev_class 的 第三个函数首地址:0x472d58
dev_class 的 第四个函数首地址:0x472d5c
dev_class 的 第五个函数首地址:0x472d60
dev_class 的虚函数表结束标志: 0
This is dev_class's v_func1()
This is dev_class's v_func2()
This is base_class's v_func3()
This is dev_class's v_func4()
This is dev_class's v_func5()

复制代码

 

通过上面的例子的尝试和输出结果,我们可以得出下面的布局图示:

可以看出当派生类中 dev_class 中重写了父类 base_class 的前两个虚函数(v_func1,v_func2)之后,使用派生类的虚函数指针代替了父类的虚函数。未重写的父类虚函数位置没有发生变化。

不知道看到这里,你心里有没有一个小问题?至少我是有的。看下面的代码:

virtual void v_func1()
{base_class::v_func1();cout << "This is dev_class's v_func1()" << endl;
}

既然派生类的虚函数表中用 dev_class::v_func1 指针代替了 base_class::v_func1,假如我显示的调用 base_class::v_func1,会不会有错呢?答案是没错的,可以正确的调用!不是覆盖了吗?dev_class 已经不知道 base_class::v_func1 的指针了,怎么调用的呢?
如果你想知道原因,请关注这两个帖子:

http://stackoverflow.com/questions/11426970/why-can-a-derived-class-virtual-function-call-a-base-class-virtual-fuction-how

http://topic.csdn.net/u/20120711/14/fa9cfba2-8814-4119-8290-99e6af2c21f4.html?seed=742904136&r=79093804#r_79093804

四、多重继承下的虚函数表


4.1子类没有重写父类的虚函数

复制代码

#include <iostream>
using namespace std;class base_class1
{
public:virtual void bc1_func1(){cout << "This is bc1_func1's v_func1()" << endl;}
};class base_class2
{
public:virtual void bc2_func1(){cout << "This is bc2_func1's v_func1()" << endl;}
};class dev_class : public base_class1, public base_class2
{
public:virtual void dc_func1(){cout << "This is dc_func1's dc_func1()" << endl;}
};int main()
{dev_class dc;cout << "dc 的虚函数表 bc1_vt 地址:" << (int*)&dc << endl;cout << "dc 的虚函数表 bc1_vt 第一个虚函数地址:" << (int*)*(int*)&dc+0 << endl;cout << "dc 的虚函数表 bc1_vt 第二个虚函数地址:" << (int*)*(int*)&dc+1 << endl;cout << "dc 的虚函数表 bc1_vt 结束标志:" << *((int*)*(int*)&dc+2) << endl;cout << "dc 的虚函数表 bc2_vt 地址:" << (int*)&dc+1 << endl;cout << "dc 的虚函数表 bc2_vt 第一个虚函数首地址::" << (int*)*((int*)&dc+1)+0 << endl;cout << "dc 的虚函数表 bc2_vt 结束标志:" << *((int*)*((int*)&dc+1)+1) << endl;// 通过函数指针调用函数,验证正确性typedef void(*func_pointer)(void);func_pointer fp = NULL;// bc1_vtfp = (func_pointer)*((int*)*(int*)&dc+0);fp();fp = (func_pointer)*((int*)*(int*)&dc+1);fp();// bc2_vtfp = (func_pointer)*(((int*)*((int*)&dc+1)+0));fp();return 0;
}

复制代码

输出结果:

复制代码

dc 的虚函数表 bc1_vt 地址:0x22ff08
dc 的虚函数表 bc1_vt 第一个虚函数地址:0x472d38
dc 的虚函数表 bc1_vt 第二个虚函数地址:0x472d3c
dc 的虚函数表 bc1_vt 结束标志:-4
dc 的虚函数表 bc2_vt 地址:0x22ff0c
dc 的虚函数表 bc2_vt 第一个虚函数首地址::0x472d48
dc 的虚函数表 bc2_vt 结束标志:0
This is bc1_func1's v_func1()
This is dc_func1's dc_func1()
This is bc2_func1's v_func1()

复制代码

通过上面的例子的尝试和输出结果,我们可以得出下面的布局图示:


可以看出:多重继承的情况,会为每一个基类建一个虚函数表。派生类的虚函数放到第一个虚函数表的后面。

陈皓在他的文章中有这么一句话:“这个结束标志(虚函数表)的值在不同的编译器下是不同的。在WinXP+VS2003下,这个值是NULL。而在Ubuntu 7.10 + Linux 2.6.22 + GCC 4.1.3下,这个值是如果1,表示还有下一个虚函数表,如果值是0,表示是最后一个虚函数表。”。那么,我在 Windows 7 + Code::blocks 10.05 下尝试,这个值是如果是 -4,表示还有下一个虚函数表,如果是0,表示是最后一个虚函数表。
我在 Windows 7 + vs2010 下尝试,两个值都是 0 。

4.2子类重写了父类的虚函数

复制代码

#include <iostream>
using namespace std;class base_class1
{
public:virtual void bc1_func1(){cout << "This is base_class1's bc1_func1()" << endl;}virtual void bc1_func2(){cout << "This is base_class1's bc1_func2()" << endl;}
};class base_class2
{
public:virtual void bc2_func1(){cout << "This is base_class2's bc2_func1()" << endl;}virtual void bc2_func2(){cout << "This is base_class2's bc2_func2()" << endl;}
};class dev_class : public base_class1, public base_class2
{
public:virtual void bc1_func1(){cout << "This is dev_class's bc1_func1()" << endl;}virtual void bc2_func1(){cout << "This is dev_class's bc2_func1()" << endl;}virtual void dc_func1(){cout << "This is dev_class's dc_func1()" << endl;}
};int main()
{dev_class dc;cout << "dc 的虚函数表 bc1_vt 地址:" << (int*)&dc << endl;cout << "dc 的虚函数表 bc1_vt 第一个虚函数地址:" << (int*)*(int*)&dc+0 << endl;cout << "dc 的虚函数表 bc1_vt 第二个虚函数地址:" << (int*)*(int*)&dc+1 << endl;cout << "dc 的虚函数表 bc1_vt 第三个虚函数地址:" << (int*)*(int*)&dc+2 << endl;cout << "dc 的虚函数表 bc1_vt 第四个虚函数地址:" << (int*)*(int*)&dc+3 << endl;cout << "dc 的虚函数表 bc1_vt 结束标志:" << *((int*)*(int*)&dc+4) << endl;cout << "dc 的虚函数表 bc2_vt 地址:" << (int*)&dc+1 << endl;cout << "dc 的虚函数表 bc2_vt 第一个虚函数首地址::" << (int*)*((int*)&dc+1)+0 << endl;cout << "dc 的虚函数表 bc2_vt 第二个虚函数首地址::" << (int*)*((int*)&dc+1)+1 << endl;cout << "dc 的虚函数表 bc2_vt 结束标志:" << *((int*)*((int*)&dc+1)+2) << endl;// 通过函数指针调用函数,验证正确性typedef void(*func_pointer)(void);func_pointer fp = NULL;// bc1_vtfp = (func_pointer)*((int*)*(int*)&dc+0);fp();fp = (func_pointer)*((int*)*(int*)&dc+1);fp();fp = (func_pointer)*((int*)*(int*)&dc+2);fp();fp = (func_pointer)*((int*)*(int*)&dc+3);fp();// bc2_vtfp = (func_pointer)*(((int*)*((int*)&dc+1)+0));fp();fp = (func_pointer)*(((int*)*((int*)&dc+1)+1));fp();return 0;
}

复制代码

 

输出结果:

复制代码

dc 的虚函数表 bc1_vt 地址:0x22ff08
dc 的虚函数表 bc1_vt 第一个虚函数地址:0x472e28
dc 的虚函数表 bc1_vt 第二个虚函数地址:0x472e2c
dc 的虚函数表 bc1_vt 第三个虚函数地址:0x472e30
dc 的虚函数表 bc1_vt 第四个虚函数地址:0x472e34
dc 的虚函数表 bc1_vt 结束标志:-4
dc 的虚函数表 bc2_vt 地址:0x22ff0c
dc 的虚函数表 bc2_vt 第一个虚函数首地址::0x472e40
dc 的虚函数表 bc2_vt 第一个虚函数首地址::0x472e44
dc 的虚函数表 bc2_vt 结束标志:0
This is dev_class's bc1_func1()
This is base_class1's bc1_func2()
This is dev_class's bc2_func1()
This is dev_class's dc_func1()
This is dev_class's bc2_func1()
This is base_class2's bc2_func2()

复制代码

通过上面的例子的尝试和输出结果,我们可以得出下面的布局图示:


是不是感觉很乱?其实一点都不乱!就是两个单继承而已。把多余的部分(派生类的虚函数)增加到第一个虚函数表的最后,CB(Code::Blocks)是这样实现的。我试了一下,vs2010不是这样实现的,读者可以自己尝试一下。本文只针对 CB 来探讨。

有人觉得多重继承不好理解。我想如果你明白了它的虚函数表是怎么样的,也就没什么不好理解了吧。
也许还有人会说,不同的编译器实现方式是不一样的,我去研究某一种编译器的实现有什么意义呢?我个人理解是这样的:1.实现方式是不一样的,但是它们的实现结果是一样的(多态)。2.无论你了解虚函数表或者不了解虚函数表,我相信你都很少会用到它。但是当你了解了它的实现机制之后,你再去看多态,再去写虚函数的时候[作为你一个coder],相信你的感觉是不一样的。你会感觉很透彻,不会有丝毫的犹豫。3.学习编译器这种处理问题的方式(思想),这才是最重要的。[好像扯远了,^-^]。
如果你了解了虚函数表之后,可以通过虚函数表直接访问类的方法,这种访问是不受成员的访问权限限制的(private,protected)。这样做是很危险的,但是确实是可以这样做的。这也是C++为什么很危险的语言的一个原因……

看完之后,你不是产生了许多其他的问题呢?至少我有了几个问题[我这人问题特别多。^-^]比如:
1.访问权限是怎么实现的?编译器怎么知道哪些函数是public,哪些是protected?
2.虚函数调用是通过虚函数表实现的,那么非虚成员函数存放在哪里?是怎么实现的呢?
3.类的成员存放在什么位置?怎么继承的呢?[这是对象布局问题,=.=]
你知道的越多,你感觉你知道的越少。推荐大家一本书吧,《深度探索C++对象模型》(英文名字是《Inside to C++ Object Model》),看完你会明白很多。


 

感谢阅读,下面列出参考资料[顺便给大家推荐一下陈皓的博客吧:http://coolshell.cn/,经常去逛逛,会学到很多,至少我是这样觉得的。^-^]:
1.http://blog.csdn.net/haoel/article/details/1948051/
2.http://baike.baidu.com/view/3750123.htm 
3.http://www.cnblogs.com/wirelesser/archive/2008/03/09/1097463.html


http://chatgpt.dhexx.cn/article/KNA8RygJ.shtml

相关文章

虚函数和虚函数表

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

C++ 虚函数表 vfptr

前言 大家都应该知道C的精髓是虚函数吧? 虚函数带来的好处就是: 可以定义一个基类的指针, 其指向一个继承类, 当通过基类的指针去调用函数时, 可以在运行时决定该调用基类的函数还是继承类的函数. 虚函数是实现多态(动态绑定)/接口函数的基础. 可以说: 没有虚函数, C将变得一…

c++ 虚函数及虚函数表

多态”的关键在于通过基类指针或引用调用一个虚函数时&#xff0c;编译时不确定到底调用的是基类还是派生类的函数&#xff0c;运行时才确定。 #include <iostream> using namespace std; class A { public:int i;virtual void func() {}virtual void func2() {} }; cla…

虚函数表结构

虚函数表 所谓虚函数表就是存放着当前类中所有虚函数地址的表。在实例化一个具有虚函数的类时&#xff0c;这个表也被分配到这个实例对象的内存中&#xff0c;通过虚函数表可以指明所要调用的函数的位置。在C编译器中虚函数表的地址存放在对象的最前面&#xff0c;这是为了即使…

关于虚函数与虚函数表

首先&#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…