探索C++对象模型之 多重继承与虚函数表

一 多重继承

//警告:我只是便于说明多重继承的内存布局,所以没在基类写虚析构函数。如果要使用指向动态创建的派生类对象的指针销毁派生类对象,则必须要在基类写虚析构函数。一般基类有虚函数,则要在基类写虚析构函数。所以给出警告。开发环境是visual studio 2010

1) 代码:

#include <iostream>
using namespace std;

class B1
{
public:
    int x;
    virtual void v1(){ cout << "B1::v1" << endl; }
    void f1(){cout << "B1::f1" << endl; }
};

class B2
{
public:
    int y;
    virtual void v2(){ cout << "B2::v2" << endl; }
    void f2(){ cout << "B2::f2" << endl; }
};

class B3
{
public:
    int z;
    virtual void v3(){ cout << "B3::v3" << endl; }
    void f3(){ cout << "B3::f3" << endl; }
};

class D : public B1, public B2, public B3
{
public:
    int a;
    void v3(){ cout << "D::v3" << endl; }
    virtual void vD(){ cout << "D::vD" << endl; }
};

2)类图:

探索C++对象模型之 多重继承与虚函数表_第1张图片

3)可视化内存布局表示:

探索C++对象模型之 多重继承与虚函数表_第2张图片

4)代码验证:

在Visual studio 2010中,虚函数表作了优化,表尾不存放类型信息,表尾的元素内容为NULL。

typedef void (*Fun)();

void PrintMember(int* pMember)
{
	cout<<*pMember<<endl;
}

void PrintVtbl(int* VtblAddress)
{
	int* ppVirtualFunction=VtblAddress;
	while(*ppVirtualFunction !=NULL)
	{
		(*(Fun*)(ppVirtualFunction))();
		++ppVirtualFunction;
	}
}

void PrintVtblAndMember(B1* pDobj)
{
	int* pRoot=(int*)pDobj;
	int* pVptrB1=pRoot+0;
	int* VtblB1Adress=(int*)*pVptrB1;
	PrintVtbl(VtblB1Adress);

	int* pMb1=pRoot+1;
	PrintMember(pMb1);

	int* pVptrB2=pRoot+2;
	int* VtblB2Address=(int*)*pVptrB2;
	PrintVtbl(VtblB2Address);

	int* pMb2=pRoot+3;
	PrintMember(pMb2);

	int* pVptrB3=pRoot+4;
	int* VtblB3Address=(int*)*pVptrB3;
	PrintVtbl(VtblB3Address);

	int* pMb3=pRoot+5;
	PrintMember(pMb3);
}

void Test()
{
	B1 *pB1 = new D();
	D *pD = dynamic_cast<D*>(pB1);
	pD->x = 10;
	pD->y = 20;
	pD->z = 30;
	pD->a = 40;
	PrintVtblAndMember(pD);
	delete pD;
	pD=NULL;
};


5) 验证代码运行结果:

探索C++对象模型之 多重继承与虚函数表_第3张图片


6)总结:

与单继承相同的是所有的虚函数地址都包含在虚函数表中,所不同的多重继承有多个虚函数表,当子类对父类的虚函数有重写时,子类的函数覆盖父类的函数在对应的虚函数位置,当子类有新的虚函数时,这些虚函数被加在第一个虚函数表的后面。

二 多重继承运行时类型转化

1)代码验证:

void TestDynamicCast()
{
    B1 *pB1 = new D();
    cout << "B1:" << pB1 << endl;
    D *pD = dynamic_cast<D*>(pB1);
    cout << "D:"<< pD << endl;
    B2 *pB2 = dynamic_cast<B2*>(pB1);
    cout << "B2:" << pB2 << endl;
    B3 *pB3 = dynamic_cast<B3*>(pB1);
    cout << "B3:" << pB3 << endl;
    delete pD;
	pD=NULL;
}


2)验证代码的运行结果:

探索C++对象模型之 多重继承与虚函数表_第4张图片

3)总结:

从多重继承的内存布局,我们可以看到子类新加入的虚函数被加到了第一个基类的虚函数表,所以当dynamic_cast的时候,子类和第一个基类的地址相同,不需要移动指针,但是当dynamic_cast到其他的父类的时候,需要做相应的指针的移动。



你可能感兴趣的:(探索C++对象模型之 多重继承与虚函数表)