【C++】:菱形继承和虚拟继承

简介: 【C++】:菱形继承和虚拟继承

1. 复杂的菱形继承及菱形虚拟继承

1.1 菱形继承

单继承:一个子类只有一个直接父类时称这个继承关系为单继承

/单继承
class Person
{
protected:
  string _name;
};
class Student : public Person
{
protected:
  int _age;
};
class PostGraduate : public Student
{
  int GPA;
};

多继承:一个子类有两个或以上直接父类时称这个继承关系为多继承

class Student
{
protected:
  int _num;
};
class Teacher
{
protected:
  int _id;
};
class Assistant : public Student, public Teacher
{
protected:
  string _majorCourse;
};

菱形继承:菱形继承是多继承的一种特殊情况

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; // 主修课程
};

菱形继承的问题:从下面的对象成员模型构造,可以看出菱形继承有数据冗余和二义性的问题。在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; // 主修课程
};
void Test()
{
  // 这样会有二义性无法明确知道访问的是哪一个
  Assistant a;
  a._name = "peter";
  // 需要显示指定访问哪个父类的成员可以解决二义性问题,但是数据冗余问题无法解决
  a.Student::_name = "xxx";
  a.Teacher::_name = "yyy";
}

1.2 虚拟继承

虚拟继承可以解决菱形继承的二义性和数据冗余的问题。如上面的继承关系,在Student和
Teacher的继承Person时使用虚拟继承,即可解决问题。需要注意的是,虚拟继承不要在其他地方去使用。

在继承关系的前面加上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";
}

虚拟继承要加在直接继承的子类上

1.3 虚拟继承解决数据冗余和二义性的原理

为了方便观察,可以先建立一个简易的菱形继承的模型,通过内存窗口进一步观察。

class A
{
public:
  int _a;
};
class B :  public A
{
public:
  int _b;
};
class C :  public A
{
public:
  int _c;
};
class D : public B, public C
{
public:
  int _d;
};
int main()
{
  D d;
  d.B::_a = 1;
  d.C::_a = 2;
  d._b = 3;
  d._c = 4;
  d._d = 5;
  return 0;
}

通过下面的图示就可以看到数据的冗余:

上述是没有进行虚拟继承的菱形继承,那么如果加上虚拟继承会是怎么样子呢?

可以看到加上虚拟继承之后,在内存窗口中就多了两个莫名其妙的地址,那么这两个地址是用来干嘛的呢?我们不妨来观察一下:

这里可以分析出D对象中将A放到的了对象组成的最下面,这个A同时属于B和C,那么B和C如何去找到公共的A呢?

这里是通过了B和C的两个指针,指向的一张表。这两个指针叫虚基表指针,这两个表叫虚基表。虚基表中存的偏移量。通过偏移量可以找到下面的A。

朋友们、伙计们,美好的时光总是短暂的,我们本期的的分享就到此结束,欲知后事如何,请听下回分解~,最后看完别忘了留下你们弥足珍贵的三连喔,感谢大家的支持!  

目录
相关文章
|
7天前
|
编译器 C++
【C++】详解C++的继承
【C++】详解C++的继承
|
5天前
|
存储 编译器 数据安全/隐私保护
|
12天前
|
C++
C++职工管理系统(类继承、文件、指针操作、中文乱码解决)
C++职工管理系统(类继承、文件、指针操作、中文乱码解决)
13 0
C++职工管理系统(类继承、文件、指针操作、中文乱码解决)
|
13天前
|
C++
【C++】学习笔记——继承_2
【C++】学习笔记——继承_2
15 1
|
14天前
|
编译器 C++
C++中的继承
C++中的继承
18 1
|
17天前
|
C++
C++一分钟之-继承与多态概念
【6月更文挑战第21天】**C++的继承与多态概述:** - 继承允许类从基类复用代码,增强代码结构和重用性。 - 多态通过虚函数实现,使不同类对象能以同一类型处理。 - 关键点包括访问权限、构造/析构、菱形问题、虚函数与动态绑定。 - 示例代码展示如何创建派生类和调用虚函数。 - 注意构造函数初始化、空指针检查和避免切片问题。 - 应用这些概念能提升程序设计和维护效率。
22 2
|
5天前
|
存储 编译器 C++
C++基础知识(六:继承)
多态是面向对象编程的四大基本原则之一,它让程序能够以统一的接口处理不同的对象类型,从而实现了接口与实现分离,提高了代码的灵活性和复用性。多态主要体现在两个层面:静态多态(编译时多态,如函数重载)和动态多态(运行时多态,主要通过虚函数实现)。
|
12天前
|
程序员 编译器 C++
【c++】继承
【c++】继承
9 0
|
13天前
|
安全 Java 程序员
【C++航海王:追寻罗杰的编程之路】继承你学会了么?
【C++航海王:追寻罗杰的编程之路】继承你学会了么?
10 0
|
13天前
|
程序员 编译器 C++
【C++】学习笔记——继承_1
【C++】学习笔记——继承_1
11 0