【C++进阶学习】C++中的继承(3)

简介: 【C++进阶学习】C++中的继承(3)

七、菱形继承和虚拟继承


  • 单继承:


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


  • 示图:


  • 多继承:

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

  • 示图:


  • 菱形继承:

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

  • 示图:


  • 菱形继承的问题:

从下面的对象成员模型构造,可以看出菱形继承有数据冗余和二义性的问题:在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";
}


  • 菱形虚拟继承:

虚拟继承可以解决菱形继承的二义性和数据冗余的问题

注:虚拟继承不要在其他地方使用

  • 示例:


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";
}


菱形继承的内存对象成员模型:

这里可以看到数据冗余,存在二义性需要显示调用


示图:


菱形虚拟继承的内存对象成员模型:

这里可以分析出D对象中将A放到的了对象组成的最下面,这个A同时属于B和C,这里是通过B和C的两个指针,指向的一张表(这两个指针叫虚基表指针,这两个表叫虚基表),虚基表中第一个位置存的是当前位置距离类自己虚函数表指针的偏移量,第二个位置存的是距离继承对象的成员变量偏移量


示图:


过程示图:


总结:

对于多继承,菱形继承和菱形虚拟继承,底层实现很复杂,所以一般不建议设计出多继承,一定不要设计出菱形继承,否则在复杂度及性能上都有问题


多继承可以认为是C++的缺陷之一,很多后来的OO语言都没有多继承(如java)


八、继承和组合


  • 概念:


  1. public继承是一种is-a的关系:


假设B继承了A,每个B对象就是一个A对象(每个派生类对象都是一个基类对象)


继承允许你根据基类的实现来定义派生类的实现


在继承方式中,基类的内部细节对子类可见


继承一定程度破坏了基类的封装,基类的改变,对派生类有很大的影响(派生类和基类间的依赖关系很强,耦合度高)


组合是一种has-a的关系:

假设B组合了A,每个B对象中都有一个A对象(优先使用对象组合,而不是类继承 )


对象组合是类继承之外的另一种复用选择,新的更复杂的功能可以通过组装或组合对象来获得


对象组合要求被组合的对象具有良好定义的接口,因为对象的内部细节是不可见的


组合类之间没有很强的依赖关系,耦合度低,优先使用对象组合有助于你保持每个类被封装


使用总结:

实际中尽量多去用组合(组合的耦合度低,代码维护性好)


有些关系就适合继承那就用继承(强相关),另外要实现多态,也必须要继承


如果类之间的关系可以用继承,可以用组合,就用组合


示例:


// Car和BMW Car和Benz构成is-a的关系
class Car {
protected:
  string _colour = "白色"; // 颜色
  string _num = "陕ABIT00"; // 车牌号
};
class BMW : public Car {
public:
  void Drive() { cout << "好开-操控" << endl; }
};
class Benz : public Car {
public:
  void Drive() { cout << "好坐-舒适" << endl; }
};
// Tire和Car构成has-a的关系
class Tire {
protected:
  string _brand = "Michelin"; // 品牌
  size_t _size = 17; // 尺寸
};
class Car {
protected:
  string _colour = "白色"; // 颜色
  string _num = "陕ABIT00"; // 车牌号
  Tire _t; // 轮胎
};


九、继承相关面试题


  1. 什么是菱形继承?菱形继承的问题是什么?


  • 菱形继承:


一个子类继承了两个父类,而这两个父类又继承了一个相同的类,这样的继承关系如同菱形


菱形继承问题:

存在数据冗余和二义性的问题


什么是菱形虚拟继承?如何解决数据冗余和二义性的

菱形虚拟继承:

用来解决菱形继承的数据冗余和二义性


如何解决:

菱形虚拟继承会让父类生成一张虚基表,并将虚基表的地址存在其成员变量中,虚基表中存储了其父类成员变量距离该基表的距离,根据距离找到其父类变量,并且两份虚基表共同指向一份父类变量


继承和组合的区别?什么时候用继承?什么时候用组合

区别:

继承相当于每个派生类对象都是一个基类对象;继承中的基类内部实现对派生类可见(一定程度破坏基类的封装性),可以根据基类的实现来决定派生类的实现(耦合度高,不利于维护)


组合相当于每个派生类对象有一个基类对象;组合中的基类内部具体实现对派生类不可见(封装性好),基类的实现和派生类的实现依赖性低(耦合度低,利于维护)


使用情形:

如果类型之间关系符合强相关的,或者需要实现多态的,用继承


如果类型之间关系符合弱相关的,或者继承和组合都可以使用的,用组合


如何设计一个不能被继承的类?

将该类的构造函数的访问权限设置为私有,当派生类调用构造函数时,会先调用父类的构造函数,而父类的构造函数不能被调用,无法构造父类对象也就构造不了派生类对象(但这样的类不仅派生类无法构造,该类自己也不能构造)


在C++11之后,我们可以将父类用 final 修饰,这样就可以达成目的


相关文章
|
1月前
|
C++ 开发者
C++学习之继承
通过继承,C++可以实现代码重用、扩展类的功能并支持多态性。理解继承的类型、重写与重载、多重继承及其相关问题,对于掌握C++面向对象编程至关重要。希望本文能为您的C++学习和开发提供实用的指导。
57 16
|
1月前
|
编译器 数据安全/隐私保护 C++
【C++面向对象——继承与派生】派生类的应用(头歌实践教学平台习题)【合集】
本实验旨在学习类的继承关系、不同继承方式下的访问控制及利用虚基类解决二义性问题。主要内容包括: 1. **类的继承关系基础概念**:介绍继承的定义及声明派生类的语法。 2. **不同继承方式下对基类成员的访问控制**:详细说明`public`、`private`和`protected`继承方式对基类成员的访问权限影响。 3. **利用虚基类解决二义性问题**:解释多继承中可能出现的二义性及其解决方案——虚基类。 实验任务要求从`people`类派生出`student`、`teacher`、`graduate`和`TA`类,添加特定属性并测试这些类的功能。最终通过创建教师和助教实例,验证代码
50 5
|
2月前
|
算法 网络安全 区块链
2023/11/10学习记录-C/C++对称分组加密DES
本文介绍了对称分组加密的常见算法(如DES、3DES、AES和国密SM4)及其应用场景,包括文件和视频加密、比特币私钥加密、消息和配置项加密及SSL通信加密。文章还详细展示了如何使用异或实现一个简易的对称加密算法,并通过示例代码演示了DES算法在ECB和CBC模式下的加密和解密过程,以及如何封装DES实现CBC和ECB的PKCS7Padding分块填充。
67 4
2023/11/10学习记录-C/C++对称分组加密DES
|
3月前
|
编译器 C++ 开发者
【C++】继承
C++中的继承是面向对象编程的核心特性之一,允许派生类继承基类的属性和方法,实现代码复用和类的层次结构。继承有三种类型:公有、私有和受保护继承,每种类型决定了派生类如何访问基类成员。此外,继承还涉及构造函数、析构函数、拷贝构造函数和赋值运算符的调用规则,以及解决多继承带来的二义性和数据冗余问题的虚拟继承。在设计类时,应谨慎选择继承和组合,以降低耦合度并提高代码的可维护性。
42 1
【C++】继承
|
4月前
|
编译器 C语言 C++
配置C++的学习环境
【10月更文挑战第18天】如果想要学习C++语言,那就需要配置必要的环境和相关的软件,才可以帮助自己更好的掌握语法知识。 一、本地环境设置 如果您想要设置 C++ 语言环境,您需要确保电脑上有以下两款可用的软件,文本编辑器和 C++ 编译器。 二、文本编辑器 通过编辑器创建的文件通常称为源文件,源文件包含程序源代码。 C++ 程序的源文件通常使用扩展名 .cpp、.cp 或 .c。 在开始编程之前,请确保您有一个文本编辑器,且有足够的经验来编写一个计算机程序,然后把它保存在一个文件中,编译并执行它。 Visual Studio Code:虽然它是一个通用的文本编辑器,但它有很多插
|
4月前
|
C++
C++番外篇——对于继承中子类与父类对象同时定义其析构顺序的探究
C++番外篇——对于继承中子类与父类对象同时定义其析构顺序的探究
73 1
|
4月前
|
C++
C++番外篇——虚拟继承解决数据冗余和二义性的原理
C++番外篇——虚拟继承解决数据冗余和二义性的原理
64 1
|
4月前
|
安全 编译器 程序员
C++的忠实粉丝-继承的热情(1)
C++的忠实粉丝-继承的热情(1)
34 0
|
4月前
|
编译器 C++
C++入门11——详解C++继承(菱形继承与虚拟继承)-2
C++入门11——详解C++继承(菱形继承与虚拟继承)-2
57 0
|
2天前
|
编译器 C语言 C++
类和对象的简述(c++篇)
类和对象的简述(c++篇)