我们由上篇博客介绍了继承的分类,并用文字解释了他们,今天我们将从它们的底层内存分布模板来进一步了解与学习。
单继承:一个类只有一个直接父类
单继承实例:
class A
{
public:
int _a;
};
class B:public A
{
public:
int _b;
};
void funtest()
{
B b;
b._a = 2;
b._b = 1;
}
int main()
{
funtest();
system("pause");
return 0;
}
我们给派生类中的数据成员赋上初值来方便我们了解派生类对象的各个数据成员在内存中的存储,如图
多继承:一个类有两个或两个以上直接父类
多继承实例:
class A
{
public:
int _a;
};
class C
{
public:
int _c;
};
class D
{
public:
int _d;
};
class B:public A,public C,public D
{
public:
int _b;
};
void funtest()
{
B b;
b._a = 1;
b._c = 2;
b._d = 3;
b._b = 4;
}
int main()
{
funtest();
system("pause");
return 0;
}
我们给派生类中的数据成员赋上初值来方便我们了解派生类对象的各个数据成员在内存中的存储,如图
菱形继承:(钻石继承)
程序实例:
class A
{
public:
int _a;
};
class C:public A
{
public:
int _c;
};
class D:public A
{
public:
int _d;
};
class B:public C,public D
{
public:
int _b;
};
void funtest()
{
B b;
b.C::_a = 1;
b.D::_a = 2;
b._c = 3;
b._d = 4;
b._b = 5;
}
int main()
{
funtest();
system("pause");
return 0;
}
我们给派生类中的数据成员赋上初值来方便我们了解派生类对象的各个数据成员在内存中的存储,如图
由于菱形继承在访问数据成员时会产生二义性和数据冗余问题,于是我们想出了虚继承的方法。
继承时采用关键字virtual来修饰基类。
虚继承实例:
class A
{
public:
int _a;
};
class B:virtual public A
{
public:
int _b;
};
void funtest()
{
B b;
b._a = 1;
b._b = 2;
}
int main()
{
funtest();
system("pause");
return 0;
}
我们给派生类中的数据成员赋上初值来方便我们了解派生类对象的各个数据成员在内存中的存储,如图
问题一:虚拟继承的构造函数做了什么事情?
构造函数由系统自动合成,将偏移量表格的地址放入派生类对象的前4个字节。
问题二:虚拟继承与直接继承有什么区别?
1.将偏移量表格的地址放入派生类对象的前4个字节
2.虚拟继承的底层实现我们可以看到,首先push了一个1,作为虚拟继承的标志
问题三:菱形虚拟继承(解决菱形继承的二义性问题)
菱形虚拟继承实例:
class A
{
public:
int _a;
};
class C:virtual public A
{
public:
int _c;
};
class D:virtual public A
{
public:
int _d;
};
class B:public C,public D
{
public:
int _b;
};
void funtest()
{
B b;
b._a = 1;
b._c = 3;
b._d = 4;
b._b = 5;
}
int main()
{
funtest();
system("pause");
return 0;
}
我们给派生类中的数据成员赋上初值来方便我们了解派生类对象的各个数据成员在内存中的存储
菱形虚拟继承的大小,用sizeof求字节数
得到D为24个字节,由上图的内存分布可以很容易的得出结论