C++虚继承分析及对象模型
C++虚继承分析及对象模型
虚继承和虚基类的定义是非常的简单的,但是在C++语言中虚继承作为一个比较生僻的但是又是绝对必要的组成部份而存在着,并且其行为和模型均表现出和一般的继承体系之间的巨大的差异(包括访问性能上的差异),现在我们对虚继承和虚基类进行研究。
定义:
虚继承:在继承定义中包含了virtual关键字的继承关系;
虚基类:在虚继承体系中的通过virtual继承而来的基类,需要注意的是:
classCSubClass : virtual public CBase {}; 其中CBase称之为CSubClass的虚基类,而不是说CBase就是个虚基类,因为CBase还可以不不是虚继承体系中的基类。
语法:
语法有语言的本身的定义所决定,总体上来说非常的简单,如下:
class CSubClass : public virtual CBaseClass {};
其中可以采用public、protected、private三种不同的继承关键字进行修饰,只要确保包含virtual就可以了,这样一来就形成了虚继承体系,同时CBaseClass就成为了CSubClass的虚基类了。
其实并没有那么的简单,如果出现虚继承体系的进一步继承会出现什么样的状况呢?如下所示:
class Base
{
public:
Base(int n)
{
this->m_base=n;
}
virtualvoid f(){cout<<"Base:f()"<<endl;}
intm_base;
};
class SubBase1:virtualpublic Base
{
public:
SubBase1(int a):Base(a)
{
this->m_subBase1=a;
}
virtualvoid g(){cout<<"SubBase1::g()"<<endl;}
int m_subBase1;
};
class SubBase2:virtualpublic Base
{
public:
SubBase2(int a):Base(a)
{
this->m_subBase2=a;
}
virtualvoid h(){cout<<"SubBase1::h()"<<endl;}
int m_subBase2;
};
classDerived:public SubBase1, public SubBase2
{
public:
Derived(int b):Base(b),SubBase1(b),SubBase2(b)
{
m_derived=b;
}
virtualvoid k(){cout<<"Derived:k()"<<endl;}
intm_derived;
};
classSubDerived:public Derived
{
public:
SubDerived(int k):Base(k),Derived(k)
{
}
};
注意上面代码中的Derived和SubDerived两个类的构造函数初始化列表中的内容。可以发现其中均包含了虚基类Base的初始化工作,如果没有这个初始化语句就会导致编译时错误,为什么会这样呢?
实验环境(Window7 X64 +Visual studio 2010)
模型分析:
单一虚拟继承:
测试代码:
#include"stdafx.h"
#include<iostream>
usingnamespacestd;
class Base
{
public:
Base(int n)
{
this->m_base=n;
}
virtualvoid f(){cout<<"Base:f()"<<endl;}
intm_base;
};
class SubBase1:virtualpublic Base
{
public:
SubBase1(int a):Base(a)
{
this->m_subBase1=a;
}
virtualvoid g(){cout<<"SubBase1::g()"<<endl;}
int m_subBase1;
};
int _tmain(intargc, _TCHAR* argv[])
{
/*Derived d(5);
SubDerivedf(6);*/
Fun pFun=NULL;
SubBase1 sb1(5);
SubBase1*sp=&sb1;
cout<<"sizeof(SubBase1) = "<<sizeof(sb1)<<endl;
cout<<"the address of vfptr_SubBase1: "<<(int*)*(int*)sp<<endl;
pFun=(Fun)*((int*)*(int*)sp);
pFun();
cout<<"the address of vfptr_Base: "<<(int*)*((int*)sp+3)<<endl;
pFun=(Fun)*((int*)*((int*)sp+3));
pFun();
system("pause");
return 0;
}
运行结果:
对象内存模型图:(SubBase1对象模型)
多重虚拟继承:
// demo.cpp : 定¡§义°?控?制?台¬¡§应®|用®?程¨¬序¨°的Ì?入¨?口¨²点Ì?。¡ê
//
#include"stdafx.h"
#include<iostream>
usingnamespacestd;
class Base
{
public:
Base(int n=1)
{
this->m_base=n;
}
virtualvoid f(){cout<<"Base:f()"<<endl;}
intm_base;
};
class SubBase1:virtualpublic Base
{
public:
SubBase1(int a=2)
{
this->m_subBase1=a;
}
virtualvoid g(){cout<<"SubBase1::g()"<<endl;}
int m_subBase1;
};
class SubBase2:virtualpublic Base
{
public:
SubBase2(int a=3)
{
this->m_subBase2=a;
}
virtualvoid h(){cout<<"SubBase2::h()"<<endl;}
int m_subBase2;
};
classDerived:public SubBase1, public SubBase2
{
public:
Derived(int b=4)
{
m_derived=b;
}
virtualvoid k(){cout<<"Derived:k()"<<endl;}
intm_derived;
};
classSubDerived:public Derived
{
public:
SubDerived(int k)
{
}
};
typedefvoid (*Fun)(void);
int _tmain(intargc, _TCHAR* argv[])
{
/*Derived d(5);
SubDerivedf(6);*/
Fun pFun=NULL;
Derived sb1(8);
Derived *sp=&sb1;
cout<<"sizeof(SubBase1) = "<<sizeof(sb1)<<endl;
cout<<"the address of vfptr_SubBase1: "<<(int*)*(int*)sp<<endl;
pFun=(Fun)*((int*)*(int*)sp);
pFun();
cout<<"the address of vfptr_Deriverd: "<<(int*)((int*)*(int*)sp+1)<<endl;
pFun=(Fun)*((int*)((int*)*(int*)sp+1));
pFun();
cout<<"the address of vfptr_SubBase2: "<<(int*)*((int*)sp+3)<<endl;
pFun=(Fun)*((int*)*((int*)sp+3));
pFun();
cout<<"the address of vfptr_Base: "<<(int*)*((int*)sp+7)<<endl;
pFun=(Fun)*((int*)*((int*)sp+7));
pFun();
system("pause");
return 0;
}
结果:
对象内存模型图:(Deirved对象模型)
实验结论:
1、虚拟继承与普通继承不同,把虚拟基类放在对象内存最后部分。各个子类通过偏移量找到这个虚基类。
2、虚拟继承中多了一个vbptr指针(虚基类表指针),指向虚拟基类。
3、vbptr指向一张表,里面放着地址偏移量。
项目 |
说明 |
第一项:到本类偏移量 |
-4:有虚函数时;0:无虚函数 |
第二项:到虚基类偏移量 |
|
第三项:是0 |
起分割作用。因为在实际内存中,SubBase1和SubBase2的这两张虚基类表示放在一起的。 |