首页 > 代码库 > C++ 对象的内存布局—— 虚继承下的虚函数

C++ 对象的内存布局—— 虚继承下的虚函数

C++ 对象的内存布局(下)这篇文章单一虚拟继承钻石型虚拟继承时的类内存布局讲得不太清楚,我有一处疑问,我用的是VS2005。因此记录一下。

 

类继承图例如以下:

技术分享

这里:类B被类B1和B2虚拟继承,而B1和B2同一时候被D继承。
B1的f()、B2的f()覆盖了B的f();
D的f()覆盖了B1的f(),D的f1()覆盖了B1的f1()
D的f()覆盖了B2的f(),D的f2()覆盖了B2的f2()

类代码例如以下:

class B
{
    public:
        int ib;
        char cb;
    public:
        B():ib(0),cb(‘B‘) {}
 
        virtual void f() { cout << "B::f()" << endl;}
        virtual void Bf() { cout << "B::Bf()" << endl;}
};
class B1 :  virtual public B//虚拟继承,钻石型继承
{
    public:
        int ib1;
        char cb1;
    public:
        B1():ib1(11),cb1(‘1‘) {}
 
        virtual void f() { cout << "B1::f()" << endl;}
        virtual void f1() { cout << "B1::f1()" << endl;}
        virtual void Bf1() { cout << "B1::Bf1()" << endl;}
 
};
class B2:  virtual public B
{
    public:
        int ib2;
        char cb2;
    public:
        B2():ib2(12),cb2(‘2‘) {}
 
        virtual void f() { cout << "B2::f()" << endl;}
        virtual void f2() { cout << "B2::f2()" << endl;}
        virtual void Bf2() { cout << "B2::Bf2()" << endl;}
       
};
 
class D : public B1, public B2
{
    public:
        int id;
        char cd;
    public:
        D():id(100),cd(‘D‘) {}
 
		virtual void f() { cout << "D::f()" << endl;}
		virtual void f1() { cout << "D::f1()" << endl;}
        virtual void f2() { cout << "D::f2()" << endl;}
        virtual void Df() { cout << "D::Df()" << endl;}
};

(1)单一虚拟继承:B1虚拟继承自B

查看类B1的内存布局的代码例如以下:

{
	int** pVtab = NULL;
	Fun pFun = NULL;

	B1 bb1;

	pVtab = (int**)&bb1;

	cout << "sizeof(B1) = " << sizeof(B1) << endl;

	cout << "[0] B1::_vfptr->" << endl;
	for (int i = 0; ; i++)
	{
		pFun = (Fun)pVtab[0][i];
		cout << ‘\t‘ << "[" << i << "] 0x" << (int*)pFun << "\t";
		if (pFun == NULL)
		{
			cout << endl;
			break;
		}
		pFun();
	}

	cout << "[1] B1::_vbptr->" << endl;
	cout << "\t" << "[0] B1dB1bptrB1 " << pVtab[1][0] << endl;
	cout << "\t" << "[1] B1dB1bptrB " << pVtab[1][1] << endl;

	cout << "[2] B1::ib1 = " << (int)pVtab[2] << endl;
	cout << "[3] B1::cb1 = " << (char)pVtab[3] << endl;

	cout << "[4] = 0x" << (int*)pVtab[4]/*[0]*/ << endl;

	cout << "[5] B::_vfptr->" << endl;	//
	cout << ‘\t‘ << "[" << 0 << "] thunk = 0x" << (int*)pVtab[5][0] << endl;
	for (int i = 1; ; i++)
	{
		pFun = (Fun)pVtab[5][i];
		cout << ‘\t‘ << "[" << i << "] 0x" << (int*)pFun << "\t";
		if (pFun == NULL)
		{
			cout << endl;
			break;
		}
		pFun();
	}
	
	cout << "[6] B::ib = " << (int)pVtab[6] << endl;
	cout << "[7] B::ic = " << (char)pVtab[7] << endl;
}

代码输出了类B1的布局:

sizeof(B1) = 32
[0] B1::_vfptr->
        [0] 0x0041106E  B1::f1()
        [1] 0x004110AF  B1::Bf1()
        [2] 0x00000000
[1] B1::_vbptr->
        [0] B1dB1bptrB1 -4
        [1] B1dB1bptrB 16
[2] B1::ib1 = 11
[3] B1::ic1 = 1

[4] = 0x00000000     //VC++中的一个NULL分隔符把B和B1的布局分开
[5] B::_vfptr->
        [0] 0x00411055  //奇怪的地方:这个不是函数B1::f()的地址。由于运行这个地址的函数会出错,那么整个B1类的B1::f()虚函数在哪里呢?难道是“调整块”的地址?
        [1] 0x004111F4  B::Bf()
        [2] 0x00000000
[6] B::ib = 0
[7] B::ic = B


(2)钻石型虚拟继承

查看D的内存布局的代码例如以下:

{
	int** pVtab = NULL;
	Fun pFun = NULL;

	D d;

	pVtab = (int**)&d;

	cout << "sizeof(D) = " << sizeof(D) << endl;
	
	cout << "[0] D::B1::_vfptr->" << endl;
	for (int i = 0; ; i++)
	{
		pFun = (Fun)pVtab[0][i];
		cout << ‘\t‘ << "[" << i << "] 0x" << (int*)pFun << "\t";
		if (pFun == NULL)
		{
			cout << endl;
			break;
		}
		pFun();
	}

	cout << "[1] D::B1::_vbptr->" << endl;
	cout << "\t" << "[0] DdB1bptrB1 " << pVtab[1][0] << endl;
	cout << "\t" << "[1] DdB1bptrB " << pVtab[1][1] << endl;

	cout << "[2] B1::ib1 = " << (int)pVtab[2] << endl;
	cout << "[3] B1::cb1 = " << (char)pVtab[3] << endl;

	cout << "[4] D::B2::_vfptr->" << endl;
	for (int i = 0; ; i++)
	{
		pFun = (Fun)pVtab[4][i];
		cout << ‘\t‘ << "[" << i << "] 0x" << (int*)pFun << "\t";
		if (pFun == NULL)
		{
			cout << endl;
			break;
		}
		pFun();
	}

	cout << "[5] D::B2::_vbptr->" << endl;
	cout << "\t" << "[0] DdB2bptrB2 " << pVtab[5][0] << endl;
	cout << "\t" << "[1] DdB1bptrB " << pVtab[5][1] << endl;

	cout << "[6] B2::ib2 = " << (int)pVtab[6] << endl;
	cout << "[7] B2::cb2 = " << (char)pVtab[7] << endl;

	cout << "[8] D::id = " << (int)pVtab[8] << endl;
	cout << "[9] D::cd = " << (char)pVtab[9] << endl;

	cout << "[10] = 0x" << (int*)pVtab[10]/*[0]*/ << endl;

	cout << "[11] D::B::_vfptr->" << endl;

	cout << ‘\t‘ << "[" << 0 << "] 0x" << (int*)pVtab[11][0] << endl;//奇怪的地方
	for (int i = 1; ; i++)
	{
		pFun = (Fun)pVtab[11][i];
		cout << ‘\t‘ << "[" << i << "] 0x" << (int*)pFun << "\t";
		if (pFun == NULL)
		{
			cout << endl;
			break;
		}
		pFun();
	}

	cout << "[12] B::ib = " << (int)pVtab[12] << endl;
	cout << "[13] B::ic = " << (char)pVtab[13] << endl;
}

 代码输出了类D的布局:

sizeof(D) = 56
[0] D::B1::_vfptr->
        [0] 0x00411163  D::f1()
        [1] 0x004110AF  B1::Bf1()
        [2] 0x004110D2  D::Df()
        [3] 0x00000000
[1] D::B1::_vbptr->
        [0] DdB1bptrB1 -4
        [1] DdB1bptrB 40
[2] B1::ib1 = 11
[3] B1::cb1 = 1

[4] D::B2::_vfptr->
        [0] 0x0041101E  D::f2()
        [1] 0x00411159  B2::Bf2()
        [2] 0x00000000
[5] D::B2::_vbptr->
        [0] DdB2bptrB2 -4
        [1] DdB1bptrB 24
[6] B2::ib2 = 12
[7] B2::cb2 = 2

[8] D::id = 100
[9] D::cd = D

[10] = 0x00000000   //VC++中的一个NULL分隔符把B和B1和B2的布局分开
[11] D::B::_vfptr->
        [0] 0x004110C3    //奇怪的地方:这个不是函数D::f()的地址。由于运行这个地址的函数会出错,那么整个D类的D::f()虚函数在哪里呢?难道是“调整块”的地址?
        [1] 0x004111F4  B::Bf()
        [2] 0x00000000
[12] B::ib = 0
[13] B::cb = B


眼下我还不清楚是怎么回事,不知道是不是thunk之类的原因。

參考:C++ 对象的内存布局(下)

C++ 对象的内存布局—— 虚继承下的虚函数