继承(单继承、多继承、菱形继承、虚继承)

一、单继承
单继承是一种 一个子类只有一个直接父类 的继承关系。
eg:

二、多继承
多继承是一种 一个子类有两个或两个以上直接父类 的继承关系。
eg:

三、菱形继承
菱形继承由两个(或以上)单继承,一个多继承构成,结构如下:
eg:

显然,上例中Assistant类多继承了Student和Teacher两个类,而Student和Teacher两个类又都单继承了Person类。其对象模型如下:


因此,在Assistant的对象中,就会有两份分别由Student和Teacher继承来的Person成员。也就是说,菱形继承中存在二义性和数据冗余问题。

#include <iostream>
using namespace std;
class Person
{
public:
       string _name;
};
class Student : public Person
{
protected:
       int _num;
};
class Teacher : public Person
{
protected:
       int _id;
};
class Assistant : public Student,public Teacher
{
protected:
       string _majorCourse;
};
void Test()
{
       Assistant a;
       //a._name = "xiaoxu"      存在二义性,出错
       a.Student::_name = "xiaoxu";    //必须显示指定访问哪个父类的成员
       a.Teacher::_name = "duxiaoxu";  //虽可解决,但此时有数据冗余的问题
}


四、虚继承
虚继承是为了 解决菱形继承的二义性和数据冗余问题 而设计 的,其特点如下:
  1. 虚继承解决了在菱形继承体系里子类对象包含多份父类对象的数据冗余和空间浪费问题。
  2. 虚继承体系看起来便复杂,在实际应用中我们通常不会定义如此复杂的继承体系。
  3. 使用虚继承会在解决数据冗余问题的同时带来性能上的损耗。
同样以上例进行讲解时,实现虚继承需要在Student和Teacher类的定义中加virtual关键词,形式如 class Student : virtual public Person class Teacher : virtual public Person 。如此定义后再在测试函数中运行 a._name="xiaoxu"; 就可以编译通过,当然,利用 a.Student::_name="xiaoxu"; 也可以编译通过,只不过此时在Student中对_name的改变,也会影响到Teacher中的_name(一改全改)。
换个简单的例子看一下虚继承的实质:

#include <iostream>
using namespace std;
class A
{
public:
       int _a;
};
class B :virtual public A
{
public:
       int _b;
};
class C : virtual public A
{
public:
       int _c;
};
class D : public B,public C
{
public:
       int _d;
};
void Test()
{
       D d;
       d.B::_a = 1;
       d.C::_a = 2;
       d._b = 3;
       d._c = 4;
       d._d = 5;
}

对于上述代码,以及上述代码中去除virtual关键词的代码,在vs2013中用窗口内存中观察可以发现:

可以看出,虚继承在解决二义性时引入了偏移量,在找A时,利用其与虚基表指针的偏移量来进行。

(注:+4是因为类型都为int型)
需要注意的是:
1.在本例中,虚基表指针占据了8个字节的空间,同时为D节省了A的4个字节的空间。
2.当A的空间够大时,只用虚继承是节省空间的。
3.sizeof(B)的结果是12而不是8,整个D的sizeof结果是4+8+8+4=20。








猜你喜欢

转载自blog.csdn.net/gunqu_d/article/details/78540655