C++虚继承与普通继承的区别

虚继承的时候在子类的对象中会多出一个叫虚类指针的大小,有的资料说这个指针指向的内存里面包含了该子类的偏移量和到基类的距离。但是我跟踪过这段内存,发现里面的数据没有规律,也找不到更多的支撑材料,权且先知道子类的对象里面会有这么一个东西吧。

先总结虚拟继承中比较特殊的地方,希望能够对大家有所帮助:

虚继承时子类的虚函数不再是添加到父类部分的虚表中,而在普通的继承中确实直接添加到父类的虚表中,这就意味着如果虚继承中子类父类都有各自的虚函数,在子类里面就会多出一个虚表指针,而普通的继承却不会这样。代码说明:

class B
{
public:
 char b[3];
public:
 virtual void bb()
 {
  cout<<"B"<<endl;
 }
};

class C:public virtual B
{
public:
 char c[3];
public:
 virtual void cc()
 {
  cout<<"C"<<endl;
 }
};

int main()
{
 C c;

 c.c[0]=1;
 c.c[1]=2;
 c.c[3]=3;

 c.b[0]=4;
 c.b[1]=5;
 c.b[2]=6;

 C*ptr=&c;
 void (*PtrFun)();
 int *p,*p2;

 memcpy(&p,&ptr,4);//使得p指向c的地址
 memcpy(&p2,p,4);  //使得p2指向虚表的地址
 memcpy(&PtrFun,p2,4);
 PtrFun();//调用C的虚函数表明前四个字节是C的虚函数表的位置

 p+=1;
 //跳过C的虚类指针
 p+=1;
 //跳过C的数组地址
 p+=1;

 memcpy(&p2,p,4);//定位到B的虚表地址
 memcpy(&PtrFun,p2,4);  //调用B的虚函数                 
 PtrFun();

 cout<<sizeof(c)<<endl;
}

普通继承中子类中则不会多出虚类指针,子类也不会有自己单独的虚函数列表,子类的虚函数会被嵌到基类部分的虚函数表的后面,代码如下:

int main()
{
 C c;

 c.c[0]=1;
 c.c[1]=2;
 c.c[3]=3;

 c.b[0]=4;
 c.b[1]=5;
 c.b[2]=6;

 C*ptr=&c;
 void (*PtrFun)();
 int *p,*p2;

 memcpy(&p,&ptr,4);//使得p指向c的地址
 memcpy(&p2,p,4);  //使得p2指向虚表的地址
 memcpy(&PtrFun,p2,4);
 PtrFun();//调用C的虚函数表明前四个字节是C的虚函数表的位置

 p2+=1;
 memcpy(&PtrFun,p2,4);
 PtrFun();

 cout<<sizeof(c)<<endl;
}


从中我们不难发现虚拟继承父类子类的内存排列方式也有很大的差别,普通继承中父类在前,子类在后;虚拟继承中先是子类部分的内存,接着再是父类的内存。对于虚拟继承中的多次继承就更奇葩了。

附上代码,大家应该很容易看明白其中的内存是如何分布的:

#include <iostream>
using namespace std;

class A
{
public:
 char a[3];
public:
 virtual void ba()
 {
  cout<<"A"<<endl;
 }
};

class B:public virtual  A
{
public:
 char b[3];
public:
 virtual void bb()
 {
  cout<<"B"<<endl;
 }
};

class C:public virtual B
{
public:
 char c[3];
public:
 virtual void cc()
 {
  cout<<"C"<<endl;
 }
};

int main()
{
 C c;
 C*ptr=&c;
 void (*PtrFun)();
 int *p,*p2;

 memcpy(&p,&ptr,4);//使得p指向c的地址
 memcpy(&p2,p,4);  //使得p2指向虚表的地址
 memcpy(&PtrFun,p2,4);
 PtrFun();//调用C的虚函数表明前四个字节是C的虚函数表的位置

 p+=1;
 //跳过C的虚类指针
 p+=1;
 //跳过C的数组地址
 p+=1;

 memcpy(&p2,p,4);//我觉得应该是到B的地址了,但是确实到A的地址
 memcpy(&PtrFun,p2,4);                    //大概是将A放到了前面吧
 PtrFun();//调用A的虚函数,表明此时的p所保存的是A的虚函数表的位置

 p+=1;
 //跳过A的数组地址
 p+=1;

 memcpy(&p2,p,4);
 memcpy(&PtrFun,p2,4);
    PtrFun();//调用B的虚函数,表明此时的p所保存的是B的虚表地址

 c.c[0]=1;
 c.c[1]=2;
 c.c[3]=3;

 c.b[0]=4;
 c.b[1]=5;
 c.b[2]=6;

 c.a[0]=7;
 c.a[1]=8;
 c.a[2]=9;
}

没错 C的内存是在前面,但是后面紧接着的不是B的内存而是C的内存,至于为什么会这样分配,这个我也不知道,希望知道的人可以指点一下。(普通继承下是 C B A)

猜你喜欢

转载自www.linuxidc.com/Linux/2015-06/119029.htm