c++虚表(vftable)、虚函数指针(vfptr)、虚基指针(vbptr)的测试结果


在VS中 --> 项目 --> 项目属性 --> C/C++ --> 命令行
添加编译选项
/d1reportSingleClassLayoutB
(B是你要查看的类名) 

在GCC下测试 vfptr 和 vbptr 会合并,在windows下是分开的

 

代码一:测试虚标的存在

///
///  @filename   
///  @author     whao Luo
///  @email      haohb13@gmail.com
///  @date       
///

#if 0
//测试虚表的存在

#include <iostream>
using namespace std;
class A
{
	int i = 10;
	int ia = 100;
	void func() {}
	virtual void run() { cout << "A::run()" << endl; }
	virtual void run1() { cout << "A::run1()" << endl; }
	virtual void run2() { cout << "A::run2()" << endl; }
};
class B : public A
{
	virtual void run() { cout << "B::run()" << endl; }
	virtual void run1() { cout << "B::run1()" << endl; }
};
class C :public A
{
	virtual void run() { cout << "C::run()" << endl; }
	virtual void run1() { cout << "C::run1()" << endl; }
	virtual void run3() { cout << "C::run3()" << endl; }
};
class D :/*virtual*/ public A
{
	virtual void run() { cout << "D::run()" << endl; }
	virtual void run1() { cout << "D::run1()" << endl; }
	virtual void run2() { cout << "D::run2()" << endl; }
	virtual void run3() { cout << "D::run3()" << endl; }
};

int test()
{
	cout << sizeof(A) << endl
		 << sizeof(B) << endl
		 << sizeof(C) << endl
		 << sizeof(D) << endl;
	cout << sizeof(long long) << endl;
	//A * pA = new D;
	D d;
	//d.run();

	typedef void(*Function)(void);

	int ** pVtable = (int **)&d;

#if 0
	int * pVtable = (int*)&d;
	int vtaleAdress = *pVtable;

	int * ppVtable = (int*)vtaleAdress;
	int func1 = *ppVtable;

	Function f1 = (Function)func1;
	f1()
#endif
	//pVtable[0][0]

	for (int idx = 0; pVtable[0][idx] != NULL; ++idx)
	{
		Function f = (Function)pVtable[0][idx];
		f();
	}

	//cout << (int)pVtable[1] << endl;
	//cout << (int)pVtable[2] << endl;

	getchar();
	return 0;
}

int main(void)
{
	test();
	return 0;
}

#endif

#if 0
// 测试一:单个虚继承,不带虚函数
//	虚继承与继承的区别
//	1. 多了一个虚基指针
//	2. 虚基类位于派生类存储空间的最末尾

// 测试二:单个虚继承,带虚函数
//   1.如果派生类没有自己的虚函数,此时派生类对象不会产生
//    虚函数指针
//   2.如果派生类拥有自己的虚函数,此时派生类对象就会产生自己本身的虚函数指针,
//     并且该虚函数指针位于派生类对象存储空间的开始位置
//

#pragma vtordisp(off)
#include <iostream>
using std::cout;
using std::endl;

class A
{
public:
	A() : _ia(10){}

	virtual
	void f()
	{	cout << "A::f()" << endl;	}
private:
	int _ia;
};

class B
:  /*virtual*/ public A
{
public:
	B() : _ib(20){}

	void fb()
	{	cout << "A::fb()" << endl;	}


	virtual void f()
	{	cout << "B::f()" << endl;	}

#if 1
	virtual void fb2()	
	{	cout << "B::fb2()" << endl;	}
#endif

private:
	int _ib;
};

int main(void)
{
	cout << sizeof(A) << endl;
	cout << sizeof(B) << endl;
	B b;
	getchar();
	return 0;
}

#endif

                   

测试一:不带虚继承,不带虚函数

结果:内存空间只有变量

1>class B size(8):
1> +---
1> 0 | +--- (base class A)
1> 0 | | _ia
1> | +---
1> 4 | _ib
1> +---
1>

 

 测试二:不带虚继承,基类带虚函数

结果:基类会带有一个虚函数指针

虚指针

1>class B size(12):
1> +---
1> 0 | +--- (base class A)
1> 0 | | {vfptr}
1> 4 | | _ia
1> | +---
1> 8 | _ib
1> +---

虚函数表

1>B::$vftable@:
1> | &B_meta
1> | 0
1> 0 | &B::f
1> 1 | &B::fb2

 

测三:单个虚继承,不带虚函数
结果:虚继承与继承的区别
  1. 多了一个虚基指针
  2. 虚基类位于派生类存储空间的最末尾

1>class B size(12):
1> +---
1> 0 | {vbptr} --> 虚基指针
1> 4 | _ib
1> +---
1> +--- (virtual base A)
1> 8 | _ia
1> +---

 

测试四:单个虚继承,带虚函数
结果一:.如果派生类没有独立的虚函数,此时派生类对象不会产生 虚函数指针

1>class B size(16):
1> +---
1> 0 | {vbptr}
1> 4 | _ib
1> +---
1> +--- (virtual base A)
1> 8 | {vfptr}
1>12 | _ia
1> +---

1>B::$vftable@:
1> | -8
1> 0 | &B::f
1>

 

结果二:如果派生类拥有自己的虚函数,此时派生类对象就会产生自己本身的虚函数指针,并且该虚函数指针位于派生类对象存储空间的最开始位置(放在了虚基指针的前面,为了加快虚函数的查找速度)

1>class B size(20):
1> +---
1> 0 | {vfptr}           // 在GCC中测试时,vfptr和vbptr会合并
1> 4 | {vbptr}
1> 8 | _ib
1> +---
1> +--- (virtual base A)
1>12 | {vfptr}
1>16 | _ia
1> +---

1>B::$vftable@B@:
1> | &B_meta
1> | 0
1> 0 | &B::fb2

1>B::$vftable@A@:
1> | -12
1> 0 | &B::f
1>

 

代码二:多重继承

///
///  @filename   
///  @author     whao Luo
///  @email      haohb13@gmail.com
///  @date       
///

// 测试三:多重继承(带虚函数)
// 1. 每个基类都有自己的虚函数表
//  2. 派生类如果有自己的虚函数,会被加入到第一个虚函数表之中
//	3.  内存布局中, 其基类的布局按照基类被声明时的顺序进行排列
// 4. 派生类会覆盖基类的虚函数,只有第一个虚函数表中存放的是
//		真实的被覆盖的函数的地址;其它的虚函数表中存放的并不是真实的
//		对应的虚函数的地址,而只是一条跳转指令
#if 0
#pragma vtordisp(off)
#include <iostream>

using std::cout;
using std::endl;

class Base1
{
public:
	Base1() : _iBase1(10){}
	virtual void f()
	{	cout << "Base1::f()" << endl;	}

	virtual void g()
	{	cout << "Base1::g()" << endl;	}

	virtual void h()
	{	cout << "Base1::h()" << endl;	}
private:
	int _iBase1;
};

class Base2
{
public:
	Base2() : _iBase2(100){}
	virtual void f()
	{
		cout << "Base2::f()" << endl;
	}

	virtual void g()
	{
		cout << "Base2::g()" << endl;
	}

	virtual void h()
	{
		cout << "Base2::h()" << endl;
	}
private:
	int _iBase2;
};

class Base3
{
public:
	Base3() : _iBase3(1000){}
	virtual void f()
	{
		cout << "Base3::f()" << endl;
	}

	virtual void g()
	{
		cout << "Base3::g()" << endl;
	}

	virtual void h()
	{
		cout << "Base3::h()" << endl;
	}
private:
	int _iBase3;
};


class Derived
	: virtual public Base1
	, public Base2
	, public Base3
{
public:
	Derived() : _iDerived(10000){}
	void f()
	{	cout << "Derived::f()" << endl;	}

	virtual void g1()
	{	cout << "Derived::g1()" << endl;	}

private:
	int _iDerived;	
};

int main(void)
{
	Derived d;
	Base2 * pBase2 = &d;
	Base3 * pBase3 = &d;
	Derived * pDerived = &d;

	pBase2->f();
	cout << "sizeof(d) = "<< sizeof(d) << endl;

	cout << "&Derived = " << &d << endl;   // 这三个地址值是不一样的
	cout << "pBase2 = " << pBase2 << endl; //
	cout << "pBase3 = " << pBase3 << endl; //

	getchar();

	return 0;
}

#endif

  


测试五:多重继承(带虚函数)

结果:
  1. 每个基类都有自己的虚函数表
  2. 派生类如果有自己的虚函数,会被加入到第一个虚函数表之中
  3. 内存布局中, 其基类的布局按照基类被声明时的顺序进行排列
  4. 派生类会覆盖基类的虚函数,只有第一个虚函数表中存放的是真实的被覆盖的函数的地址;其它的虚函数表中存放的并不是真实的对应的虚函数的地址,而只是一条跳转指令


1>class Derived size(28):
1> +---
1> 0 | +--- (base class Base1)
1> 0 | | {vfptr}
1> 4 | | _iBase1
1> | +---
1> 8 | +--- (base class Base2)
1> 8 | | {vfptr}
1>12 | | _iBase2
1> | +---
1>16 | +--- (base class Base3)
1>16 | | {vfptr}
1>20 | | _iBase3
1> | +---
1>24 | _iDerived
1> +---


1>Derived::$vftable@Base1@:
1> | &Derived_meta
1> | 0
1> 0 | &Derived::f
1> 1 | &Base1::g
1> 2 | &Base1::h
1> 3 | &Derived::g1
1>
1>Derived::$vftable@Base2@:
1> | -8
1> 0 | &thunk: this-=8; goto Derived::f 跳转指令
1> 1 | &Base2::g
1> 2 | &Base2::h
1>
1>Derived::$vftable@Base3@:
1> | -16
1> 0 | &thunk: this-=16; goto Derived::f
1> 1 | &Base3::g
1> 2 | &Base3::h

 

 


1>class Derived size(32):
1> +---
1> 0 | +--- (base class Base2)
1> 0 | | {vfptr}
1> 4 | | _iBase2
1> | +---
1> 8 | +--- (base class Base3)
1> 8 | | {vfptr}
1>12 | | _iBase3
1> | +---
1>16 | {vbptr}
1>20 | _iDerived
1> +---
1> +--- (virtual base Base1)
1>24 | {vfptr}
1>28 | _iBase1
1> +---


1>Derived::$vftable@Base2@:
1> | &Derived_meta
1> | 0
1> 0 | &Derived::f
1> 1 | &Base2::g
1> 2 | &Base2::h
1> 3 | &Derived::g1
1>
1>Derived::$vftable@Base3@:
1> | -8
1> 0 | &thunk: this-=8; goto Derived::f
1> 1 | &Base3::g
1> 2 | &Base3::h

1>Derived::$vftable@Base1@:
1> | -24
1> 0 | &thunk: this-=24; goto Derived::f
1> 1 | &Base1::g
1> 2 | &Base1::h

 


1>class D size(48):
1> +---
1> 0 | +--- (base class B1)
1> 0 | | +--- (base class B)
1> 0 | | | {vfptr}
1> 4 | | | _ib
1> 8 | | | _cb
1> | | | <alignment member> (size=3)
1> | | +---
1>12 | | _ib1
1>16 | | _cb1
1> | | <alignment member> (size=3)
1> | +---
1>20 | +--- (base class B2)
1>20 | | +--- (base class B)
1>20 | | | {vfptr}
1>24 | | | _ib
1>28 | | | _cb
1> | | | <alignment member> (size=3)
1> | | +---
1>32 | | _ib2
1>36 | | _cb2
1> | | <alignment member> (size=3)
1> | +---
1>40 | _id
1>44 | _cd
1> | <alignment member> (size=3)
1> +---

1>D::$vftable@B1@:
1> | &D_meta
1> | 0
1> 0 | &D::f
1> 1 | &B::Bf
1> 2 | &D::f1
1> 3 | &B1::Bf1
1> 4 | &D::Df
1>
1>D::$vftable@B2@:
1> | -20
1> 0 | &thunk: this-=20; goto D::f
1> 1 | &B::Bf
1> 2 | &D::f2
1> 3 | &B2::Bf2
1>

 

代码三:钻石型虚继承

///
///  @filename   
///  @author     whao Luo
///  @email      haohb13@gmail.com
///  @date       
///

// 测试四:钻石型虚继承

//虚基指针所指向的虚基表的内容:
//	1. 虚基指针的第一条内容表示的是该虚基指针距离所在的子对象的首地址的偏移
//	2. 虚基指针的第二条内容表示的是该虚基指针距离虚基类子对象的首地址的偏移
#if 1

#pragma vtordisp(off)
#include <iostream>
using std::cout;
using std::endl;

class B
{
public:
	B() : _ib(10), _cb('B'){}

	virtual void f()
	{
		cout << "B::f()" << endl;
	}

	virtual void Bf()
	{
		cout << "B::Bf()" << endl;
	}

private:
	int _ib;
	char _cb;
};
 
class B1 : virtual public B
{
public:
	B1() : _ib1(100), _cb1('1'){}

	virtual void f()
	{	cout << "B1::f()" << endl;	}	

#if 1
	virtual void f1()
	{
		cout << "B1::f1()" << endl;
	}
	virtual void Bf1()
	{
		cout << "B1::Bf1()" << endl;
	}
#endif

private:
	int _ib1;
	char _cb1;
};



class B2 : virtual public B
{
public:
	B2() : _ib2(1000), _cb2('2'){}

	virtual void f()
	{
		cout << "B2::f()" << endl;
	}
#if 1
	virtual void f2()
	{
		cout << "B2::f2()" << endl;
	}
	virtual void Bf2()
	{
		cout << "B2::Bf2()" << endl;
	}
	#endif
private:
	int _ib2;
	char _cb2;
};

class D : public B1, public B2
{
public:
	D() : _id(10000), _cd('3'){}


	virtual void f()
	{
		cout << "D::f()" << endl;
	}

#if 1
	virtual void f1()
	{
		cout << "D::f1()" << endl;
	}
	virtual void f2()
	{
		cout << "D::f2()" << endl;
	}

	virtual void Df()
	{
		cout << "D::Df()" << endl;
	}
#endif
private:
	int _id;
	char _cd;
};

int main(void)
{
	D d;
	cout << sizeof(d) << endl;
	getchar();
	return 0;
}

#endif

  


测试六:钻石型虚继承

虚基指针所指向的虚基表的内容:
  1. 虚基指针的第一条内容表示的是该虚基指针距离所在的子对象的首地址的偏移
  2. 虚基指针的第二条内容表示的是该虚基指针距离虚基类子对象的首地址的偏移
1>class D size(52):
1> +---
1> 0 | +--- (base class B1)
1> 0 | | {vfptr}
1> 4 | | {vbptr}
1> 8 | | _ib1
1>12 | | _cb1
1> | | <alignment member> (size=3)
1> | +---
1>16 | +--- (base class B2)
1>16 | | {vfptr}
1>20 | | {vbptr}
1>24 | | _ib2
1>28 | | _cb2
1> | | <alignment member> (size=3)
1> | +---
1>32 | _id
1>36 | _cd
1> | <alignment member> (size=3)
1> +---
1> +--- (virtual base B)
1>40 | {vfptr}
1>44 | _ib
1>48 | _cb
1> | <alignment member> (size=3)
1> +---
1>
1>D::$vftable@B1@:
1> | &D_meta
1> | 0
1> 0 | &D::f1
1> 1 | &B1::Bf1
1> 2 | &D::Df
1>
1>D::$vftable@B2@:
1> | -16
1> 0 | &D::f2
1> 1 | &B2::Bf2

1>D::$vftable@B@:
1> | -40
1> 0 | &D::f
1> 1 | &B::Bf


1>D::$vbtable@B1@:
1> 0 | -4
1> 1 | 36 (Dd(B1+4)B)
1>
1>D::$vbtable@B2@:
1> 0 | -4
1> 1 | 20 (Dd(B2+4)B)

 

 

(具体细节待补充)

posted @ 2020-05-15 22:48  CTHON  阅读(4268)  评论(0编辑  收藏  举报