继承的特性:
注意:类实例(即类的对象)不能直接访问类的private和protected的成员,但是可以直接访问类的public成员。
1. 子类通过public方式继承父类,则父类中的public、protected和private属性的成员在子类中依次是 public、protected和private属性,即通过public继承并不会改变父类原来的数据属性。
2. 子类通过protected方式继承父类,则父类中的public、protected和private属性的成员在 子类 中 依次 是 protected、protected和private属性,即通过protected继承原来父类中public属性降级为子类中的protected属性,其余父类属性在子类中不变。
3. 子类通过private方式继承父类,则父类中的public、protected和private属性的成员在 子类 中 依次 是 private、private和private属性,即通过private继承原来父类中public属性降级为子类中的private属性,protected属性降级为子类中的private属性,其余父类属性在子类中不变。
注意: 其实父类的原属性并未改变,只是通过 继承关系被继承到子类中的父类成员的个别属性有所变化 ,即只是在子类中父类的个别成员属性降级了,原来父类的成员属性并未变。
单继承
一个子类只有一个直接父类得继承关系为单继承
多继承
一个子类有两个或两个以上直接父类时称这个继承关系为多继承
多继承中的指针偏移问题,见下面代码示例:
#include <iostream>
using namespace std;
class Base1 { public: int b1; };
class Base2 { public: int b2; };
class Derive :public Base1, public Base2{ public: int _d; };
int main()
{
Derive d;
Base1 *p1 = &d;
Base2 *p2 = &d;
Derive *p3 = &d;
cout << "p1: " << p1 << " p2: " << p2 << " p3: " << p3 << endl; //p1 = p3 != p2
return 0;
}
这里把子类对象d的地址分别赋值给这三个指针:谁先被继承,对象模型里谁就在前面!
菱形继承
菱形继承是多继承的一种特殊情况,顾名思义菱形继承就是继承关系近似呈一个菱形的形状
即:首先有一个Person类,然后Student继承了Person,Teacher也继承了Person,然后又有一个Assistant类即继承了Student又继承了Teacher,此时它们的继承关系就是一个菱形继承关系。
菱形继承存在的问题:数据冗余和二义性
代码示例:Assistant的对象中Person成员会有两份
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; // 主修课程
};
在这样的菱形继承关系中会存在如下问题:
由于Student和Teacher类都是继承的Person,所以它们都拥有Person的_name属性,然后Assistant又同时继承了Student和Teacher类,所以就会导致在Assistant里面有两份_name,这样就导致了一个数据冗余的问题,由于Assistant里面有两个_name,一个继承自Student类,另一个继承自Teacher类,所以在访问的时候就会发送歧义,即二义性。在执行main函数中给Assistant的对象a的_name成员赋值时会报错,_name不明确,无法确定你访问的是哪一个:
可以通过显示指定访问哪一个父类的成员来一定程度的解决二义性的问题,如下所示:
虽然可以像上述这样解决二义性的问题,但是数据的冗余问题依然存在,如果Person类中再多一些属性后果可想而知。
解决数据冗余的方法:虚拟继承
虚拟继承
需要再继承时使用关键字virtual(虚拟的)
按照以上描述,修改代码并打印Assistant对象中的Student类中成员_name和Teacher类中的成员_name的地址,发现结果一致,即解决了数据冗余的问题:
***虚拟继承的底层原理***:
原来的Student和Teacher中各有一个_name,这就造成了数据的冗余和访问时的二义性,所以要解决这个问题,就只需要存一个_name就可以了,那么就不能在原来各自的位置啦,需要放到一个公共的位置(这个位置的_name同时属于Student和Teacher两个类),原来存放_name的位置存储了两个指针(虚指针),它们分别指向一块区域(虚表),这里面就存储了原来的_name在Student和Teacher中存储的位置到现在_name的位置的一个偏移量,通过这个偏移量即可找到这个_name的位置。