虚继承
在多重继承下,一个基类可以在派生层次中出现多次,在这种情况下,采用虚继承,通过虚继承指出他希望共享其虚基类的状态,无论该虚基类出现过多少次,只继承一个共享的基类对象。
虚继承的初始化:
通常每个类只初始化自己的成员,但是该策略应用于虚继承时就会导致多次初始化虚基类,采用方法:从最底层派生类的构造函数初始化虚基类,任何直接或间接继承虚基类的类一般必须为该虚基类提供自己的初始化式。
举例:class Zoo
{
public:
Zoo(int i,double d):i(i),b(d)
{
cout<<"Zooconstructed"<<endl;
}
int i;
double b;
};
class X:public virtualZoo
{
public:
X(int i1,double val1):Zoo(i1,val1){}
};
class Y:public virtualZoo
{
public:
Y(int i2,double val2):Zoo(i2,val2){}
};
class Z:public X,public Y
{
public:
Z(int i3,double val3):Zoo(i3,val3),X(i3,val3),Y(i3,val3)
{}
};
int main()
{
Z z(3,3.4);
cout<<sizeof(z)<<endl;
cout<<sizeof(double)<<endl;
getchar();
return 0;
}
对于非静态数据成员访问
• 实际上它们的访问,必须通过一个显式或者隐式的object进行。比如编译器默默传递个成员函数的this指针。并且编译器都需要吧class object的起始地址加上offset
• &origin._y 编译成 &origin + ( &Point3d::_y - 1 )
class object内存大小
• 1 nonstatic data member的总和大小;
• 2 任何由于alignment(译注)的需求而填补(padding)上去的空间;
• 3 为了支持virtual而由内部产生的任何额外负担(overhead);
空类的内存布局
class X
{
public:
X();
~ X();
private:
};
int main()
{
X x;
cout<<sizeof(x)<<endl;
/*
* 返回值为1,虽然类为空,但是编译器会安插一个字节,用来代表该类型,
*/
cout<<sizeof(int)<<endl;
//Y y;
//cout<<sizeof(y)<<endl;
getchar();
return 0;
}
继承多态与Data members内存布局和使用多态带来的额外负担
一般来说,基类在前,子类在后
对于单类而言,先声明在前,后声明在后
使用多态带来的额外负担:
只有继承没有多态的内存布局
class A
{
public:
int val;
char bit1;
};
class B: public A
{
public:
char bit2;
};
class C:public B
{
public:
char bit3;
};
int main()
{
cout<<sizeof(A)<<endl;
cout<<sizeof(B)<<endl;
cout<<sizeof(C)<<endl;
getchar();
return 0;
}
内存布局如下所示:
加上多态的内存布局
加上虚函数额外的负担:
1、导入一个virtual table,存放每一个虚函数的地址,table元素的数目是虚函数的数目加上1到2个slots(用来runtime type identification)。
2、 在每一个类对象加一个vptr,指向virtual talbe,用来实现RTTI
3、 加强constructor,为每一个vptr设定初始值,使其指向virtual table
4、 加强destructor,取消vptr
现代编译器会将vptr放在class object 起头处。
class A
{
public:
int val;
char bit1;
virtual void foo()
{
}
};
class B: public A
{
public:
char bit2;
int val2;
};
int main()
{
cout<<sizeof(A)<<endl;
cout<<sizeof(B)<<endl;
getchar();
return 0;
}
多重继承的内存布局
class A
{
public:
int val;
char bit1;
};
class B: public A
{
public:
char bit2;
int val2;
};
class C
{
public:
char val3;
};
class D:public B,public C
{
};
int main()
{
B b;
C *c;
D d;
c=&d;//c=((C*)(char*)&d)+sizeof(B);
A *a=&d;//简单拷贝i地址即可
getchar();
return 0;
}
虚拟继承的内存布局
实现上的挑战就是提供一个有效的方法把不同对象维护的那个虚基类对象折叠成一个单一的对象,并且还要保存子类与父类之间多态指针的多态赋值操作。
一般的实现方法是,分割为两个部分,一个是不变部分,是指不管后继如何演化,总是有固定的offset,这一部分数据可以直接存取,另一部分是共享局部,是指virtual base class 部分,这一部分的数据位置会不断发生变化,只可以被间接存取。
class X
{
int c;
};
class Y: public virtual X
{
int a;
};
class Z:public virtual X
{
int b;
};
class A:public Y,public Z
{
//int d;
};
int main()
{
cout<<sizeof(X)<<endl;
cout<<sizeof(Y)<<endl;
cout<<sizeof(Z)<<endl;
cout<<sizeof(A)<<endl;
getchar();
return 0;
}