C++继承总结(下)——菱形继承
一.什么是菱形继承
菱形继承是多继承的一种特殊情况,一个类有多个父类,这些父类又有相同的父类或者祖先类,那么该类就会有多份重复的成员,从而造成调用二义性和数据冗余。
class Person
{
public:
Person()
{
cout << "Person构造" << endl;
}
public:
int _name = 0;
int _age = 0;
};
class Student : public Person
{
public:
Student()
{
cout << "Student构造" << endl;
}
int _stuid = 0;
};
class Teacher : public Person
{
public:
Teacher()
{
cout << "Teacher构造" << endl;
}
int _jobid = 0;
};
class Assistant : public Student, public Teacher
{
public:
Assistant()
{
cout << "Assistant构造" << endl;
}
int _task = 0;
};
int main()
{
Assistant a;
//a._name;//二义性:访问Student的_name还是Teacher的_name呢?
//需要指定类域访问
a.Student::_name = 1;
a.Student::_age = 2;
a._stuid = 3;
a.Teacher::_name = 4;
a.Teacher::_age = 5;
a._jobid = 6;
a._task = 7;
return 0;
}
从a的内存布局可以看到,a中有两份_name和_age,它们是从Student和Teacher类继承下来的。二义性的问题可以通过指定类域访问解决,但数据冗余的问题是无法规避的,必须引入新的技术——虚继承
二.虚继承的用法
只需在继承那个祖先类时加上关键字virtual即可
class Person
{
public:
Person()
{
cout << "Person构造" << endl;
}
public:
int _name = 0;
int _age = 0;
};
class Student : virtual public Person
{
public:
Student()
{
cout << "Student构造" << endl;
}
int _stuid = 0;
};
class Teacher : virtual public Person
{
public:
Teacher()
{
cout << "Teacher构造" << endl;
}
int _jobid = 0;
};
class Assistant : public Student, public Teacher
{
public:
Assistant()
{
cout << "Assistant构造" << endl;
}
int _task = 0;
};
int main()
{
Assistant a;
a.Student::_name = 1;
a.Student::_age = 2;
a._stuid = 3;
a.Teacher::_name = 4;
a.Teacher::_age = 5;
a._jobid = 6;
a._task = 7;
return 0;
}
虚继承前:
虚继承后:
可以看到,Person构造函数只调用了一次。
再来看看虚继承后a的内存分布:
虚继承后,重复的那部分成员被单独拎了出来,只有一份,此时就不存在二义性的问题了。a.Student::_name;a.Student::_name;a._name访问的是同一份数据。同时也解决了数据冗余的问题。
三.虚继承的原理
Student和Teacher中多出的这两个东西是什么呢?这似乎是一个地址,那我们在内存中看一看(注意是小端存储,低字节存低位数据,高字节存高位数据,故地址应该为007e9b4c和007e9b54)
注意这是16进制,故第一个数 是20,第二个数是12。
在看看上面的内存分布,会发现:006ff8d0这个地址加上20,006ff8d8加上12,刚好是006ff8e4,也就是重复的Person那部分变量的起始地址。
Assistant对象中将Person放到的了对象组成的最下面,这个Person同时属于Student和Teacher,给Student和Teacher都添加一个指针,指向的一张表。这两个指针叫虚基表指针,这两个表叫虚基表。虚基表中存了偏移量,通过偏移量可以找到下面的Person。事实上,虚基表中存放了两个数据,第二个数是偏移量,第一个数与多态中的虚表有关,这里不作展开,后面的多态会讲到。