直接上代码
#include <iostream>
using namespace std;
class TestVirPtr0
{
public:
virtual void FirPrint0(void) {
cout << "TestVirPtr0->FirPrint0()" << endl;
}
virtual void FirPrint1(void) {
cout << "TestVirPtr0->FirPrint1()" << endl;
}
};
class TestVirPtr1
{
virtual void SecPrint0(void) {
cout << "TestVirPtr1->SecPrint0()" << endl;
}
virtual void SecPrint1(void) {
cout << "TestVirPtr1->SecPrint1()" << endl;
}
};
class TestVirPtr: public TestVirPtr0, public TestVirPtr1
{
virtual void Print0(void) {
cout << "TestVirPtr->Print0()" << endl;
}
virtual void Print1(void) {
cout << "TestVirPtr->Print1()" << endl;
}
};
typedef void (*print)(void);
int main(void)
{
TestVirPtr test;
/*
虚表以及虚函数在类中存储类似这样的结构
test {
void **vtable0[];
void **vtable1[];
.
.
.
void **vtablen[];
}
*/
//*(long*)test 得到第一个虚表地址,虚表存放虚函数的指针地址
long *vtable0 = (long*) (*((long*)&test+0));
//*vtable得到虚表第一个元素,即第一个虚函数
print p = (print)(*vtable0);
p(); //输出 TestVirPtr0->FirPrint0()
//虚表地址+1,得到虚表第二个元素,即第二个虚函数
print p1 = (print)(*(vtable0+1));
p1(); //输出 TestVirPtr0->FirPrint1()
//*(long*)test 得到第二个虚表地址,虚表存放虚函数的指针地址
long *vtable1 = (long*) (*((long*)&test+1));
//*vtable得到虚表第一个元素,即第一个虚函数
p = (print)(*vtable1);
p(); //输出 TestVirPtr1->SecPrint0()
//虚表地址+1,得到虚表第二个元素,即第二个虚函数
p1 = (print)(*(vtable1+1));
p1(); //输出 TestVirPtr1->SecPrint1()
/** 执行会崩溃,因为已经越界了 */
// //*(long*)test 得到第三个虚表地址,虚表存放虚函数的指针地址
// long *vtable2 = (long*) (*((long*)&test+2));
// //*vtable得到虚表第一个元素,即第一个虚函数
// p = (print)(*vtable2);
// p();
// //虚表地址+1,得到虚表第二个元素,即第二个虚函数
// p1 = (print)(*(vtable2+1));
// p1();
/**
* 父类存在虚函数的情况下,子类的虚函数不会另外增加一个虚表去管理,默认附加在第一个继承父类的虚函数后面
*/
print p2 = (print)(*(vtable0+2)); p2(); //输出 TestVirPtr->Print0()
return 0;
}输出结果:

由此代码小实验可知C++的虚表和虚函数是如何存储的。
本文通过代码示例展示了C++中虚表的存储结构和虚函数的调用方式。程序创建了一个具有多重继承和虚函数的类,并通过指针访问虚表来直接调用虚函数,揭示了虚表在多继承下的组织形式以及虚函数的实现原理。

被折叠的 条评论
为什么被折叠?



