虚函数表地址,虚函数地址,虚函数指针的地址

class Base
{
public:
    virtual void func()
        {
            ...
        }
};

Base b1;

1.怎么取到这三个地址?

以这个虚基类为例子:

(1)虚函数表的地址:*(int *)&b1 

解释:先取出b1的地址,然后转为int型指针变量,作为一个4个字节的指针变量,之所以之么做,就是为了取到虚基类对象的内存的前4个字节作为整体,就是一个指向虚函数表的指针,所以只要在*解引用,就是虚函数表的地址了

(2)虚函数的地址(以虚函数表的第一个虚函数指针指向的虚函数为例子):

*(int *)*(int *)&b1 ,

解释:*(int *)&b1就是虚函数表的地址,然后取前4个字节作为一个int*指针,这个指针就是指向第一个虚函数的指针,最后用*解引用即可

(3)虚函数指针的地址(以虚函数表中的第二个虚函数指针的地址为例子):

(int *)*(int *)&b1+1

解释:(2)中已经说到,(int *)*(int *)&b1取到的就是指向第一个虚函数的指针,那么,我们直接让这个指针+1,也就是地址移动4个字节,就是第二个虚函数指针的地址了

2.一些重要的原理层次的测试:(具体过程,见注释)

#include<iostream>
#include<string>
using namespace std;

class Base
{
public:
	virtual void func()
	{
		cout << "Base func" << endl; //它的虚函数表应该只有一个 虚函数
	}
	virtual void func_1()
	{
		cout << "Base func_1" << endl;
	}
	
};

class D1 :public Base
{
public:
	
	//func不修改它,也就是和Base中的虚函数一摸一样
	virtual void func_2()
	{
		cout << "D1 func_2" << endl;
	}
	virtual void func_3()
	{
		cout << "D1 func_3" << endl;
	}
	//重写
	//void func()
	//{
		//cout << "D1 func" << endl;
	//}
};
//测试一个类的 内存大小:
class Test1
{
public:
	int m;
	void func()
	{
		cout << "func of Test1" << endl;
	}
};
class Test2
{
public:
	int m;
	virtual void func()
	{
		cout << "virtual func of Test2" << endl;
	}
};

//定义一个函数指针
typedef void(*Func_p)(void);

int main()
{
	//测试:
	//(1)不同虚函数表 中的同一个虚函数的 地址是否相同
	Base b1;
	D1 d1;


	//正式测试:
	//(1)不同vtable中的同一个虚函数的 地址是否相同
	cout <<"b1中的 func 虚函数的地址: "<< *(int *)*(int*)&b1 << endl;
	Func_p test1 = (Func_p)*(int *)*(int*)&b1;
	cout << "d1中的 func 虚函数的地址: " << *(int *)*(int*)&d1 << endl;
	Func_p test2 = (Func_p)*(int *)*(int*)&d1;
	//测试结果:--1:没有覆盖的话:虚函数的地址是相同的
	//          --2:如果覆盖了的话:就是两个函数了,所以地址是不相同的
	//(2)不同vtable中的同一个虚函数的指针的地址是否相同:
	cout << "b1中的 func 虚函数的指针的地址: " << (int *)*(int*)&b1 << endl;
	Func_p test3 = (Func_p)*(int *)*(int*)&b1;
	cout << "d1中的 func 虚函数的指针的地址: " << (int *)*(int*)&d1 << endl;
	Func_p test4 = (Func_p)*(int *)*(int*)&d1;
	//(3)同一个vtable中的那些虚函数指针是否相邻(虚函数本身的地址必然不相邻)
	cout << "b1中的 func 虚函数 指针的地址: " << (int *)*(int*)&b1 << endl;
	Func_p test5 = (Func_p)*(int *)*(int*)&b1;
	test5();
	cout << "b1中的 func_1 虚函数 指针的地址: " << (int*)*(int*)&b1+1 << endl;
	Func_p test6 = (Func_p)*((int *)*(int*)&b1+1);
	test6();
	//(4)多个类对象的 虚函数表的地址是否相同?
	Base b2;
	cout << "b1 的虚函数表的 地址: " << *(int *)&b1 << endl;
	cout << "b2 的虚函数表的 地址: " << *(int *)&b2 << endl;
	//结果相同
	//(5)没有虚函数的类 中自然也没有 虚表的指针,自然也不存在虚表
	//这一点用 类的内存大小可以测试
	cout << "Test1的内存大小:" << sizeof(Test1) << endl;
	cout << "Test2的内存大小:" << sizeof(Test2) << endl; //多了一个虚函数表的指针
	//(6)其实,从上面可见,子类 和 派生类中 如果没有覆盖就是同一个虚函数,但是不同的虚函数指针
	//如果覆盖了,又是2个不同的虚函数
	//不过,无论是否重写,都是基类的虚函数的指针排在虚函数表的最前面
	//?测试一下这一点
	Func_p test7 = (Func_p)*(int *)*(int*)&d1;
	test7();
	//是
	//(7)动态绑定最本质的原理:
	//当基类的指针指向派生类的对象时,通过地址移动指向的是  派生类的 虚函数表,所以调用的是派生类的虚函数
	//测试--当一个基类的指针指向派生类的对象时,证明 这个指针指向的虚函数表的地址是派生类的虚函数表的
	Base* b_p = &d1;
	cout << "b_p基类指针指向的对象的 虚函数表的地址是 : " << *(int*)&(*b_p) << endl;
	cout << "b1的虚函数表的地址是 : " << *(int *)&b1 << endl;
	cout << "d1的虚函数表的地址是: " << *(int *)&d1 << endl;


	





	//(int*)(int)&Base; //先将地址转化为int型,再转化为int*指针类型
	//cout << "b1 的地址是 :" << &b1 << endl;                 //这个取到的是 类对象的地址
	//cout << "b1的虚函数表的地址(第一个虚函数指针)取法 :" << (int*)*(int*)&b1 << endl; //这个取到的是虚函数表的地址,???--解释如下
	//悟:其实,虚函数表指针,就是一个int*类型的变量,和b1的地址相同,就是它的前4个字节,
	//而它指向的位置,才是一个表的开始,所以,先利用&取到b1的地址,然后转为(int*)也就是指针类型的变量
	//然后利用*解指针,得到它的指向的东西,它所指向的东西仍然是一个指针(第一个虚函数的指针),所以转化为(int *)类型
	//cout << "b1的第二个虚函数指针" << ((int *)*(int*)&b1) + 1 << endl;  //int*指针类型+1就是移动4个字节

	//利用函数指针调用 虚函数:
	//func_p = (int *)*(int *)&b1;
	//Func_p func_p1 = (Func_p)*(int *)*(int *)&b1;
	//func_p1();
	//Func_p func_p2 = (Func_p)*((int *)*(int *)&b1 + 1);//说明(int *)*(int *)&b1就是第一个指针的地址
	//func_p2();
	//cout << "1:" << *(int *)&b1<<endl;             //原来,这个地址是  虚函数指针的地址
	//cout << "2: " << *(int *)*(int *)&b1 << endl;  //多加了*(int*)之后并不是 原来的指向的东西了,这个地址是 虚函数的地址
	//所以,将这个地址 转换为 void (*p)(void) 类型的函数指针,然后就可以调用这个函数了



	//cout << "Base 中 func虚函数的地址是 :" << &Base::func << endl;
	//cout << "D1 中 func虚函数的 的地址是 : " << &D1::func << endl;

	//(2)同一个虚函数表中的 虚函数地址是否相邻?
	//cout << "D1中的 func_2的虚函数的地址是: " << &D1::func_2 << endl;

	return 0;
}

 

3.从以上测试代码我们可以得出以下结论:

(1)不同vtable中的同一个虚函数的 地址是否相同?

//测试结果:--1:没有覆盖的话:虚函数的地址是相同的

 //          --2:如果覆盖了的话:就是两个函数了,所以地址是不相同的

(2)不同vtable中的同一个虚函数的指针的地址是否相同:

不同!

(3)同一个vtable中的那些虚函数指针是否相邻(虚函数本身的地址必然不相邻)

--指向虚函数的指针是相邻的

(4)多个类对象的 虚函数表的地址是否相同?

--相同,说明它们都是共用的虚函数表

(5)没有虚函数的类 中自然也没有 虚表的指针,自然也不存在虚表

(6)无论是否重写,都是基类的虚函数的指针排在虚函数表的最前面

(7)动态绑定最本质的原理:
    //当基类的指针指向派生类的对象时,通过地址移动指向的是  派生类的 虚函数表,所以调用的是派生类的虚函数
    //测试--当一个基类的指针指向派生类的对象时,证明 这个指针指向的虚函数表的地址是派生类的虚函数表的

  • 1
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值