C++番外篇——虚拟继承解决数据冗余和二义性的原理

简介: C++番外篇——虚拟继承解决数据冗余和二义性的原理

首先我们先来看正常的菱形继承:

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对象:

从内存窗口可以看到d对象存在两个_a,造成了数据的冗余。


接下来看一下菱形虚拟继承:

class A
{
public:
  int _a;
};
 
class B :virtual public A
{
public:
  int _b;
};
 
class C :virtual 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类中的_a同时属于B类和C类,可是我B类和C类需要用到_a时,如何去寻找公共的_a呢?

仔细查看,虚拟继承与非虚拟继承的内存窗口对比,前者的B类与C类不但没有存_a,反而存了一个类似地址的数据,它存个这玩意干嘛呢?

既然那么像地址,那我们再调出一个内存窗口,分别查看这两个类似地址的数据到底指向何方:

发现B里面存的地址指向的数据为14(即20),C里面存的地址指向的数据为0c(即12)。

这两个数据是干嘛的呢?它存这样一个数据有何用途?

盲猜一波:

既然B类的第一行存的地址指向了14,那我就让这个地址+14,C也是同样的道理,让C里面的地址+0c:

结果两个不同的等式相加得到了相同的答案——93F9A0!

回头看,93F9A0正是_a的地址啊!


所以我们不得不得出一个结论:

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

图示:


很多人说C++语法复杂,其实多继承就是一个体现,多继承也可以认为是C++的缺陷之一。有了多继承,就存在菱形继承,有了菱形继承就有菱形虚拟继承,底层实现就很复杂,知道了虚拟继承解决数据冗余和二义性的原理似乎更加印证了这些说法。所以一般不建议设计出多继承,一定不要设计出菱形继承。否则在复杂度及性能上都有问题。

相关文章
|
15天前
|
安全 C语言 C++
彻底摘明白 C++ 的动态内存分配原理
大家好,我是V哥。C++的动态内存分配允许程序在运行时请求和释放内存,主要通过`new`/`delete`(用于对象)及`malloc`/`calloc`/`realloc`/`free`(继承自C语言)实现。`new`分配并初始化对象内存,`delete`释放并调用析构函数;而`malloc`等函数仅处理裸内存,不涉及构造与析构。掌握这些可有效管理内存,避免泄漏和悬空指针问题。智能指针如`std::unique_ptr`和`std::shared_ptr`能自动管理内存,确保异常安全。关注威哥爱编程,了解更多全栈开发技巧。 先赞再看后评论,腰缠万贯财进门。
|
2月前
|
C++ 开发者
C++学习之继承
通过继承,C++可以实现代码重用、扩展类的功能并支持多态性。理解继承的类型、重写与重载、多重继承及其相关问题,对于掌握C++面向对象编程至关重要。希望本文能为您的C++学习和开发提供实用的指导。
65 16
|
2月前
|
编译器 数据安全/隐私保护 C++
【C++面向对象——继承与派生】派生类的应用(头歌实践教学平台习题)【合集】
本实验旨在学习类的继承关系、不同继承方式下的访问控制及利用虚基类解决二义性问题。主要内容包括: 1. **类的继承关系基础概念**:介绍继承的定义及声明派生类的语法。 2. **不同继承方式下对基类成员的访问控制**:详细说明`public`、`private`和`protected`继承方式对基类成员的访问权限影响。 3. **利用虚基类解决二义性问题**:解释多继承中可能出现的二义性及其解决方案——虚基类。 实验任务要求从`people`类派生出`student`、`teacher`、`graduate`和`TA`类,添加特定属性并测试这些类的功能。最终通过创建教师和助教实例,验证代码
62 5
|
4月前
|
编译器 C++ 开发者
【C++】继承
C++中的继承是面向对象编程的核心特性之一,允许派生类继承基类的属性和方法,实现代码复用和类的层次结构。继承有三种类型:公有、私有和受保护继承,每种类型决定了派生类如何访问基类成员。此外,继承还涉及构造函数、析构函数、拷贝构造函数和赋值运算符的调用规则,以及解决多继承带来的二义性和数据冗余问题的虚拟继承。在设计类时,应谨慎选择继承和组合,以降低耦合度并提高代码的可维护性。
47 1
【C++】继承
|
5月前
|
C++
C++番外篇——对于继承中子类与父类对象同时定义其析构顺序的探究
C++番外篇——对于继承中子类与父类对象同时定义其析构顺序的探究
79 1
|
5月前
|
安全 编译器 程序员
C++的忠实粉丝-继承的热情(1)
C++的忠实粉丝-继承的热情(1)
37 0
|
5月前
|
编译器 C++
C++入门11——详解C++继承(菱形继承与虚拟继承)-2
C++入门11——详解C++继承(菱形继承与虚拟继承)-2
63 0
|
5月前
|
程序员 C++
C++入门11——详解C++继承(菱形继承与虚拟继承)-1
C++入门11——详解C++继承(菱形继承与虚拟继承)-1
66 0
|
17天前
|
编译器 C++ 开发者
【C++篇】深度解析类与对象(下)
在上一篇博客中,我们学习了C++的基础类与对象概念,包括类的定义、对象的使用和构造函数的作用。在这一篇,我们将深入探讨C++类的一些重要特性,如构造函数的高级用法、类型转换、static成员、友元、内部类、匿名对象,以及对象拷贝优化等。这些内容可以帮助你更好地理解和应用面向对象编程的核心理念,提升代码的健壮性、灵活性和可维护性。
|
20天前
|
编译器 C语言 C++
类和对象的简述(c++篇)
类和对象的简述(c++篇)