复杂的菱形继承及菱形虚拟继承(详解)

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

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

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

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

  • 菱形继承:菱形继承是多继承的一种特殊情况。(不推荐用,坑!)


菱形继承的问题:从下面的对象成员模型构造,可以看出菱形继承有数据冗余和二义性的问题。在Assistant的对象中Person成员会有两份。

clike 复制代码
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";//会报错("Assistant::_name不明确")
	// 需要显示指定访问哪个父类的成员可以解决二义性问题,但是数据冗余问题无法解决
	a.Student::_name = "xxx";
	a.Teacher::_name = "yyy";
}

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

clike 复制代码
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";
}

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

为了研究虚拟继承原理,我们给出了一个简化的菱形继承继承体系,再借助内存窗口观察对象成员的模型。

clike 复制代码
#include <iostream>
using namespace std;

class A
{
public:
	int _a;
};
// class B : public A
class B : virtual public A
{
public:
	int _b;
};
// class C : public A
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;
}

不使用虚继承(class C : public A):


使用虚继承(class C : virtual public A)

笔试面试题

  1. 什么是菱形继承?菱形继承的问题是什么?
    菱形继承是指在继承关系中存在一种特殊的结构,其中一个派生类同时继承两个基类,而这两个基类又共同继承同一个基类。这样形成的继承关系呈菱形,因此得名菱形继承。
  2. 什么是菱形虚拟继承?如何解决数据冗余和二义性的
    菱形虚拟继承是为了解决菱形继承中的数据冗余和函数二义性问题而引入的机制。通过使用虚拟继承(virtual),可以确保在菱形继承中共享基类的实例,从而避免了数据冗余的问题。
  3. 继承和组合的区别?什么时候用继承?什么时候用组合?
  • 继承(Inheritance)和组合(Composition)是面向对象编程中用于构建类之间关系的两种主要方式。

  • 继承是一种"is-a"关系,其中一个类可以继承另一个类的属性和行为。通过继承,子类可以共享父类的成员变量和成员函数,并且可以添加自己的特定功能。继承适用于具有层次结构的类,其中子类是父类的特殊化。

  • 组合是一种"has-a"关系,其中一个类包含另一个类的对象作为成员变量。通过组合,一个类可以使用其他类的对象来实现自己的功能,而不需要继承其所有特性。组合适用于在一个类中使用其他类的功能,而不需要与其形成层次结构。

  • 当需要表示一种类与类之间的层次结构、子类是父类的特殊化关系时,可以使用继承。继承可以提供代码重用和多态性的好处。当需要在一个类中使用另一个类的功能、将一个类作为另一个类的组成部分时,可以使用组合。组合可以实现代码模块化和灵活性。

  • 在选择继承或组合时,需要考虑类之间的关系和需求。如果存在"is-a"关系,且子类可以完全继承父类的属性和行为,可以选择继承。如果存在"has-a"关系,且一个类需要使用另一个类的功能,可以选择组合。此外,还应考虑代码的可维护性、扩展性和设计的灵活性。
    (本章完)

相关推荐
眠りたいです10 小时前
基于脚手架微服务的视频点播系统-服务端开发部分(补充)文件子服务问题修正
c++·微服务·云原生·架构
ULTRA??10 小时前
各种排序算法时间复杂度分析和实现和优势
c++·python·算法·排序算法
欢喜躲在眉梢里10 小时前
基于 openFuyao 社区的无硬件 UB 开发实战指南
运维·数据库·人工智能·vscode·ai·开发工具·go开发
博语小屋10 小时前
简单线程池实现(单例模式)
linux·开发语言·c++·单例模式
何妨呀~10 小时前
Linux在VMware上添加磁盘与扩展分区
linux·运维·服务器
墨雪不会编程10 小时前
C++基础语法篇八 ——【类型转换、再探构造、友元】
java·开发语言·c++
Wang's Blog10 小时前
RabbitMQ: 全面安装与运维指南之从基础部署到高级配置
运维·分布式·rabbitmq
CIb0la10 小时前
Linux 6.19-rc1 释出,龙芯为内核加入 32 位架构支持
linux·运维
yuuki23323310 小时前
【C++】内存管理
java·c++·算法
model200510 小时前
Web 服务和 SFTP 用户 操作目录
linux·运维·服务器