空类大小为1;
空类的成员:
class foo
{
?//空类包含六个成员(所有类的默认成员)
? //默认认构造函数
foo();
//默认拷贝构造函数
foo(foo &);
//默认析构函数
~foo();
//缺省赋值运算符
operator=();
//缺省取地址运算符
operator&();
//const对象的缺省取地址运算符
const operator&() const;
};
虚继承与虚函数等不同,虚继承是为了解决菱形继承的而出现的。
比如我们有父类Human,其中以public方式声明了肤色成员skin;然后我们分别用了两个子类Asian和European去继承这个父类,这样两个子类也有了自己对应的肤色成员skin以及父类的成员skin,而当我们用新的子类Japanese去同时继承两个子类的时候,这时候Japanese就拥有了两个来自Human的skin成员,便出了问题。
而解决的方案就是虚继承。在继承时加上关键字virtual,子类对象在继承时,系统会在一块连续空间中生成一个虚表(virtual table),子类中保存指向虚表的虚表指针,虚表中保存子类相对于虚基类的偏移量,虚继承的顶层父类的成员在继承时变为一个虚基类保存在子类中,在继承的时候等同于所有虚继承的子类都从虚基类中继承了同一个成员,而不是每一次都把自己的父类的成员一起继承了。
//正常的继承下成员情况
class Human{
public:
char skin;
};
//子类
class Asian : public Human
{
//size 1
public:
<base class Human>
char* A::skin;
};
class European : public Human
{
//size 1
public:
<base class Human>
char* A::skin;
};
//新的子类去继承前两个子类,发生菱形继承
class Japanese : public Asian, public European
{
//size 2
public:
//出现二义性
<base class Asian>
<base class Human>
char* A::skin;
<base class European>
<base class Human>
char* A::skin;
};
//虚继承方式下的成员
//子类
class Asian : virtual public Human
{
//size 5
public:
//虚表指针
{vtptr}
(base class Human)
skin;
};
Asian::$vbtable@:
0 --- |0
1 --- |4 (Asian(Asian+0)Human)
class European : virtual public Human
{
//size 5
public:
{vtptr}
(base class Human)
skin;
};
European::$vbtable@:
0 --- |0
1 --- |4 (European(European+0)Human)
class Japanese : virtual public Asian, virtual public European
{
//size 9
public:
<base class Aisan>
{vtptr}
<base class European>
{vtptr}
(virtual base Human)
skin;
};
D:: $vbtable@:
0 --- |0
1 --- |8 <Japanese<Asian+0>Human>;
D:: $vbtable@:
0 --- |0
1 --- |4 <Japanese<European+0>Human>;
可以通过类间指针来访问验证成员:
void *p = &Japanese;
//访问元素skin
//后半段求偏移量 先用取到子类地址,虚表中的指针步长为int。
((Human*)((char*)p + *(int*)((int*)*(int*)p+1)))->skin;
原文:https://www.cnblogs.com/stagonie/p/14903887.html