多继承
之前在https://blog.youkuaiyun.com/Jochebed666/article/details/86571445博客中主要提到了单继承,那么什么是多继承呢???
多继承即一个子类可以有多个父类,它继承了多个父类的特性。
C++ 类可以从多个类继承成员,语法如下:
class <派生类名>:<继承方式1><基类名1>,<继承方式2><基类名2>,…
{
<派生类类体>
};
其中,访问修饰符继承方式是 public、protected 或 private 其中的一个,用来修饰每个基类,各个基类之间用逗号分隔,如上所示。现在让我们一起看看下面的实例:
#include <iostream>
using namespace std;
// 基类 Shape
class Shape
{
public:
void setWidth(int w)
{
width = w;
}
void setHeight(int h)
{
height = h;
}
protected:
int width;
int height;
};
// 基类 PaintCost
class PaintCost
{
public:
int getCost(int area)
{
return area * 70;
}
};
// 派生类
class Rectangle: public Shape, public PaintCost
{
public:
int getArea()
{
return (width * height);
}
};
int main(void)
{
Rectangle Rect;
int area;
Rect.setWidth(5);
Rect.setHeight(7);
area = Rect.getArea();
// 输出对象的面积
cout << "Total area: " << Rect.getArea() << endl;
// 输出总花费
cout << "Total paint cost: $" << Rect.getCost(area) << endl;
return 0;
}
当上面的代码被编译和执行时,它会产生下列结果:
Total area: 35
Total paint cost: $2450
我们可以发现多继承跟单继承在很多地方很相似。这就是比单继承多了些父类。而多继承里有一种特殊的继承方式叫菱形继承。
菱形继承
菱形继承是多继承的一种特殊情况。
菱形继承的问题:从下面的对象成员模型构造,可以看出菱形继承有数据冗余和二义性的问题。在Assistant的对象中Person成员会有两份。
#include<iostream>
#include<string>
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 ; // 主修课程
};
int main()
{
// 这样会有二义性无法明确知道访问的是哪一个
Assistant a ;
a._name = "peter";
// 需要显示指定访问哪个父类的成员可以解决二义性问题,但是数据冗余问题无法解决
a.Student::_name = "xxx";
a.Teacher::_name = "yyy";
return 0;
}
从图中我们看到菱形继承有数据冗余的现象一个Assistant将会有两个Person,且两个Person的位置不一致,所以当改变Person其中之一,其他的Person也随之改变。
那么如何解决菱形继承的二义性和数据冗余呢???
c++中提供了虚拟继承来解决这个问题。即继承Person时使用虚拟继承。
class <派生类名>:virtual <继承方式> <基类名>
{
<派生类类体>
};
虚拟继承的原理主要是生成一个基类的偏移量,通过偏移量查找唯一的基类值,即:
class Person
{
public :
string _name ; // 姓名
};
class Student : virtual public Person
{
protected :
int _num ; //学号
};
class Teacher : virtual public Person
{
protected :
int _id ; // 职工编号
};
class Assistant : public Student, public Teacher
{
protected :
string _majorCourse ; // 主修课程
};
void Test ()
{
Assistant a ;
a._name = "peter";
}
从图中我们看到虚拟继承是通过了Student和Teacher的两个指针,指向的一张表。这两个指针叫虚基表指针,这两个表叫虚基表。虚基表中存了偏移量。通过偏移量可以找到下面的Person。虚拟继承使用偏移量,所以有消耗,当基类比较大时,可节约空间消耗,但效率有所下降。
小结一下
多继承是c++的一个缺陷。特别是会出现菱形继承和菱形虚拟继承,底层实现复杂!