例如:现在有五个类:人(Person),学生(Student),研究生(GStudent),职工(Employee),在职研究生(EGStudent)
#include
using namespace std;
#include//人
class Person
{
private:int id;
public:Person(int pid) :id(pid) { cout << "create Person" << endl; }~Person() { cout << "destory Person" << endl; }
};//学生
class Student : public Person
{
private:int s_num;
public:Student(int pid, int sn) :Person(pid), s_num(sn) { cout << "create Student" << endl; }~Student() { cout << "destory Student" << endl; }
};//研究生
class GStudent : public Student
{
private:int g_num;
public:GStudent(int pid, int sn, int gn) :Student(pid, sn), g_num(gn) { cout << "create GStudent" << endl; }~GStudent() { cout << "destory GStudent" << endl; }
};//职工
class Employee : public Person
{
private:int e_num;
public:Employee(int pid, int en) :Person(pid), e_num(en) { cout << "create Employee" << endl; }~Employee() { cout << "destory Employee" << endl; }
};//在职研究生
class EGStudent : public Employee,public GStudent
{
private:int egs_num;
public:EGStudent(int spid,int epid, int en, int gn, int sn, int egn) :Employee(epid, en), GStudent(spid, sn, gn), egs_num(egn) { cout << "create EGStudent" << endl; }~EGStudent() { cout << "destory EGStudent" << endl; }
};int main()
{EGStudent egs(1, 2, 3, 4, 5, 6);return 0;
}
我们可以看到在职研究生对象egs共有3个成员属性
我们运行一下:
我们发现研究生对象egs是一个人,但却有两个身份证id,显然在语义层面是错误的。
这种问题我们一般称为数据冗余与二义性问题,为了解决这样的问题,我们引入了虚继承的概念。
虚继承构造函数的调用顺序:
首先调用虚基类的构造函数(虚基类只会构建一次)
其次调用继承类的基类的构造函数
再调用继承类的构造函数
最后调用自己类的构造函数
我们将上述代码改为虚继承Person的方式:
#include
using namespace std;
#include//人
class Person
{
private:int id;
public:Person(int pid) :id(pid) { cout << "create Person" << endl; }~Person() { cout << "destory Person" << endl; }
};//学生
class Student : virtual public Person
{
private:int s_num;
public:Student(int pid , int sn) :Person(pid), s_num(sn) { cout << "create Student" << endl; }~Student() { cout << "destory Student" << endl; }
};//研究生
class GStudent : public Student
{
private:int g_num;
public:GStudent(int pid, int sn, int gn) :Person(pid),Student(pid,sn), g_num(gn) { cout << "create GStudent" << endl; }~GStudent() { cout << "destory GStudent" << endl; }
};//职工
class Employee : virtual public Person
{
private:int e_num;
public:Employee(int pid, int en) :Person(pid), e_num(en) { cout << "create Employee" << endl; }~Employee() { cout << "destory Employee" << endl; }
};//研究生职工
class EGStudent : public Employee,public GStudent
{
private:int egs_num;
public:EGStudent(int epid,int spid,int en, int gn, int sn, int egn) :Person(epid),Employee(epid,en), GStudent(epid, sn, gn), egs_num(egn) { cout << "create EGStudent" << endl; }~EGStudent() { cout << "destory EGStudent" << endl; }
};int main()
{EGStudent egs(1, 2, 3, 4, 5, 6);return 0;
}
我们此时可以看到在职研究生的属性成员变为了4个
我们运行一下:
我们将egs对象中Person属性的id改为10,再看一下
我们此时发现所有继承了Person类的成员属性中的Person是同一份
其原理:
我们将Person定义为虚基类后,编译器会将Person的空间放在此类对象空间的最底部,而其他继承Person的类中原本是存放Person属性成员,现在改为了距离Person类所在空间的偏移量。此时所有继承Person的类共用同一Person,解决了数据冗余与二义性问题。
①:请问D类对象的大小?
我们之前学过,空类的大小为1,存在一个占位符
占位符不会被继承,且占位符不算在类的空间大小中
因此D对象的大小为1
②:请问D类对象的大小?
因为虚继承后B和C类需要有一个存放A类偏移量(整形)的空间
且占位符不算在类的空间大小中
因此D的大小为8
上一篇: 画杨桃第二课时教学反思