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++ 对象的内存布局—— 虚继承下的虚函数,古老的榕树,5-wow.com

郑重声明:本站内容如果来自互联网及其他传播媒体,其版权均属原媒体及文章作者所有。转载目的在于传递更多信息及用于网络分享,并不代表本站赞同其观点和对其真实性负责,也不构成任何其他建议。