53-54 - 被遗弃的多重继承

news2025/1/19 3:07:25

---- 整理自狄泰软件唐佐林老师课程

1. 问题

C++是否允许一个类继承自多个父类?

1.1 C++中的 多重继承

  • C++支持编写多重继承的代码
    • 一个子类可以拥有多个父类
    • 子类拥有所有父类的成员变量
    • 子类继承所有父类的成员函数
    • 子类对象可以当作任意父类对象使用

1.2 多重继承的语法规则

在这里插入图片描述

  • 多重继承的本质单继承相同

2. 多重继承问题一

在这里插入图片描述

  • 编程实验:多重继承问题一
#include <iostream>
#include <string>

using namespace std;

class BaseA
{
    int ma;
public:
    BaseA(int a)
    {
        ma = a;
    }
    int getA()
    {
        return ma;
    }
};

class BaseB
{
    int mb;
public:
    BaseB(int b)
    {
        mb = b;
    }
    int getB()
    {
        return mb;
    }
};

class Derived : public BaseA, public BaseB
{
    int mc;
public:
    Derived(int a, int b, int c) : BaseA(a), BaseB(b)
    {
        mc = c;
    }
    int getC()
    {
        return mc;
    }
    void print()
    {
        cout << "ma = " << getA() << ", "
             << "mb = " << getB() << ", "
             << "mc = " << mc << endl;
    }
};

int main()
{
    cout << "sizeof(Derived) = " << sizeof(Derived) << endl;    // 12
    
    Derived d(1, 2, 3);
    
    d.print();
    
    cout << "d.getA() = " << d.getA() << endl;
    cout << "d.getB() = " << d.getB() << endl;
    cout << "d.getC() = " << d.getC() << endl;
    
    cout << endl;
    
    BaseA* pa = &d;
    BaseB* pb = &d;
    
    cout << "pa->getA() = " << pa->getA() << endl;
    cout << "pb->getB() = " << pb->getB() << endl;
    
    cout << endl;
    
    void* paa = pa;
    void* pbb = pb;
    
    
    if( paa == pbb )
    {
        cout << "Pointer to the same object!" << endl; 
    }
    else
    {
        cout << "Error" << endl;
    }
    
    cout << "pa = " << pa << endl;
    cout << "pb = " << pb << endl;
    cout << "paa = " << paa << endl;
    cout << "pbb = " << pbb << endl; 
    
    return 0;
}

在这里插入图片描述
在这里插入图片描述

  • 通过 多重继承 得到的对象可能拥有 “不同的地址

    • 解决方案:无

在这里插入图片描述

3. 多重继承问题二

  • 多重继承可能产生冗余的成员

在这里插入图片描述
Doctor中有两个m_name、m_age

  • 编程实验:多重继承问题二
#include <iostream>
#include <string>

using namespace std;

class People
{
    string m_name;
    int m_age;
public:
    People(string name, int age)
    {
        m_name = name;
        m_age = age;
    }
    void print()
    {
        cout << "Name = " << m_name << ", "
             << "Age = " << m_age << endl;
    }
};

class Teacher : virtual public People
{
public:
    Teacher(string name, int age) : People(name, age)
    {
    }
};

class Student : virtual public People
{
public:
    Student(string name, int age) : People(name, age)
    {
    }
};

class Doctor : public Teacher, public Student
{
public:
    Doctor(string name, int age) : Teacher(name, age), Student(name, age), People(name, age)
    {
    }
};

int main()
{
    Doctor d("Delphi", 33);
    
    d.print();
    
    return 0;
}

在这里插入图片描述

  • 当多重继承关系出现闭合时将产生数据冗余的问题

    • 解决方案:虚继承
      在这里插入图片描述
  • 虚继承能够解决 数据冗余 问题

  • 中间层父类 不再关心顶层父类的初始化(中间层父类不调用父类构造函数)

  • 最终子类 必须直接调用 顶层父类的构造函数

问题:当架构设计中需要继承时,无法确定使用直接继承还是虚继承

4. 多重继承问题三

  • 多重继承可能产生 多个虚函数表
    在这里插入图片描述

  • 编程实验:多重继承问题三

#include <iostream>
#include <string>

using namespace std;

class BaseA
{
public:
    virtual void funcA()
    {
        cout << "BaseA::funcA()" << endl;
    }
};

class BaseB
{
public:
    virtual void funcB()
    {
        cout << "BaseB::funcB()" << endl;
    }
};

class Derived : public BaseA, public BaseB
{

};

int main()
{
    Derived d;
    BaseA* pa = &d;
    BaseB* pb = &d;
    BaseB* pbe = (BaseB*)pa;    // oops!!
    BaseB* pbc = dynamic_cast<BaseB*>(pa);
    
    cout << "sizeof(d) = " << sizeof(d) << endl;
    
    cout << "Using pa to call funcA()..." << endl;
    
    pa->funcA();
    
    cout << "Using pb to call funcB()..." << endl;
    
    pb->funcB();
    
    cout << "Using pbc to call funcB()..." << endl;
    
    pbc->funcB();
    
    cout << endl;
    
    cout << "pa = " << pa << endl;
    cout << "pb = " << pb << endl;
    cout << "pbe = " << pbe << endl;
    cout << "pbc = " << pbc << endl;
    
    return 0;
}

在这里插入图片描述

需要进行强制类型转换时,C++中推荐使用新式类型转换关键字
解决方案:dynamic_cast
在这里插入图片描述
在这里插入图片描述

5. 正确的使用多重继承

  • 工程开发中的“多重继承”方式:
    • 单继承某个类 + 实现(多个)接口

在这里插入图片描述

  • 编程实验:正确的多继承方式
#include <iostream>
#include <string>

using namespace std;

class Base
{
protected:
    int mi;
public:
    Base(int i)
    {
        mi = i;
    }
    int getI()
    {
        return mi;
    }
    bool equal(Base* obj)
    {
        return (this == obj);
    }
};

class Interface1
{
public:
    virtual void add(int i) = 0;
    virtual void minus(int i) = 0;
};

class Interface2
{
public:
    virtual void multiply(int i) = 0;
    virtual void divide(int i) = 0;
};

class Derived : public Base, public Interface1, public Interface2
{
public:
    Derived(int i) : Base(i)
    {
    }
    void add(int i)
    {
        mi += i;
    }
    void minus(int i)
    {
        mi -= i;
    }
    void multiply(int i)
    {
        mi *= i;
    }
    void divide(int i)
    {
        if( i != 0 )
        {
            mi /= i;
        }
    }
};

int main()
{
    Derived d(100);
    Derived* p = &d;
    Interface1* pInt1 = &d;
    Interface2* pInt2 = &d;
    
    cout << "p->getI() = " << p->getI() << endl;    // 100
    
    pInt1->add(10);
    pInt2->divide(11);
    pInt1->minus(5);
    pInt2->multiply(8);
    
    cout << "p->getI() = " << p->getI() << endl;    // 40
    
    cout << endl;
    
    cout << "pInt1 == p : " << p->equal(dynamic_cast<Base*>(pInt1)) << endl;
    cout << "pInt2 == p : " << p->equal(dynamic_cast<Base*>(pInt2)) << endl;
    
    return 0;
}

在这里插入图片描述
在这里插入图片描述

  • 一些有用的工程建议
    • 先继承自一个父类,然后实现多个接口
    • 父类中提供equal()成员函数
    • equal()成员函数用于判断指针是否指向当前对象
    • 与多重继承相关的强制类型转换使用dynamic_cast完成

6. 小结

  • C++支持多重继承的编程方式
  • 多重继承容易带来问题:
    • 可能出现“同一个对象的地址不同”的情况
    • 虚继承可以解决数据冗余的问题
    • 虚继承使得架构设计可能出现问题
  • 多重继承中可能出现多个虚函数表指针VPTR
  • 与多重继承相关的强制类型转换用dynamic_cast完成
  • 工程开发中采用“单继承多接口”的方式使用多继承
  • 父类提供成员函数用于判断是否指向当前对象

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/44356.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

PC_非连续内存分配方式@分页存储管理@地址变换机构@快表

文章目录非连续内存分配方式&#x1f388;分页存储管理基本分页存储管理页面和页面大小分块和碎片逻辑地址结构页表页表项结构页表项和地址比较&#x1f388;页表项地址地址变换机构基本地址变换机构结构图映射过程Note:页表长度页表项长度页表大小例小结ref具有快表的地址变换…

Django学习第一天

学习建议 先从看视频学习开始&#xff0c;网上学习Django的视频有很多&#xff0c;然后跟着视频多多练习并且做一些小项目来加深印象和理解。 注意&#xff1a; 要学习Django前&#xff0c;必须先学习python基础&#xff0c;因为Django是基于python这门语言而开发的&#xff0…

openEuler快速入门-Navicat远程链接openGauss数据库

文章目录前言一、环境准备二、openGauss服务设置步骤2.1 切换至用户openGauss2.2添加放行IP2.3 修改加密方式3.4 重启openGauss服务3.5 创建远程连接角色备注总结前言 最近这段时间再整理openGauss数据库相关内容&#xff0c;在这里总结记录并分享一些基础的操作以及遇到的一些…

与堆和堆排序相关的问题

与堆和堆排序相关的问题 作者&#xff1a;Grey 原文地址&#xff1a; 博客园&#xff1a;与堆和堆排序相关的问题 CSDN&#xff1a;与堆和堆排序相关的问题 堆结构说明 堆结构就是用数组实现的完全二叉树结构&#xff0c;什么是完全二叉树&#xff1f;可以参考如下两篇博…

向善的力量:顺丰,如何在不确定性中寻求确定性

眼下新冠病毒来势汹汹&#xff0c;广大民众生活受到巨大影响。虽然物流企业也受到巨大影响&#xff0c;但面对严峻形势&#xff0c;众多物流企业依然在为保障民生献出自己的一份力量&#xff0c;在当下最大的不确定性中努力寻求确定性。 特别是在最近&#xff0c;常常看到各类…

拿捏Fiddler抓包教程(10)-Fiddler如何设置捕获Firefox浏览器的Https会话

1.简介 经过上一篇对Fiddler的配置后&#xff0c;绝大多数的Https的会话&#xff0c;我们可以成功捕获抓取到&#xff0c;但是有些版本的Firefox浏览器仍然是捕获不到其的Https会话&#xff0c;需要我们更进一步的配置才能捕获到会话进行抓包。 2.宏哥环境 1.宏哥的环境是Win…

[附源码]SSM计算机毕业设计校园兼职招聘系统JAVA

项目运行 环境配置&#xff1a; Jdk1.8 Tomcat7.0 Mysql HBuilderX&#xff08;Webstorm也行&#xff09; Eclispe&#xff08;IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持&#xff09;。 项目技术&#xff1a; SSM mybatis Maven Vue 等等组成&#xff0c;B/S模式 M…

数仓开发之DWS层(一)

目录 一&#xff1a;流量域来源关键词粒度页面浏览各窗口汇总表&#xff08;FlinkSQL&#xff09; 1.1 主要任务&#xff1a; 1.2 思路分析&#xff1a; 1.3 图解&#xff1a; 1.4 ClickHouse建表语句&#xff1a; 二&#xff1a;流量域版本-渠道-地区-访客类别粒度页面浏…

数据结构和算法之图

什么是图 定义 包含 1. 一组顶点&#xff1a;通常用V(Vertex)表示顶点集合2. 一组边&#xff1a;通常用E(Edge)表示边的集合1. 边是顶点对&#xff1a;(v,w)属于E&#xff0c;其中v,w属于V2. 有向边<v,w>表示从v指3.不考虑重边和自回路 抽象数据类型定义 1.类型名称&…

[附源码]Python计算机毕业设计SSM基于java的云顶博客系统(程序+LW)

环境配置&#xff1a; Jdk1.8 Tomcat7.0 Mysql HBuilderX&#xff08;Webstorm也行&#xff09; Eclispe&#xff08;IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持&#xff09;。 项目技术&#xff1a; SSM mybatis Maven Vue 等等组成&#xff0c;B/S模式 Maven管理等…

【车载开发系列】UDS诊断---诊断会话控制($0x10)

【车载开发系列】UDS诊断—诊断会话控制&#xff08;$0x10&#xff09; UDS诊断---诊断会话控制&#xff08;$0x10&#xff09;【车载开发系列】UDS诊断---诊断会话控制&#xff08;$0x10&#xff09;一.概念定义二.三种会话模式1&#xff09;默认会话2&#xff09;编程会话3&a…

【DevPress】V2.4.0版本发布,增加留资组件

DevPress V2.4.0版本于2022年9月29日发版 一、该版本功能包含 1、新需求 1&#xff09;企业社区移动端优化&#xff0c;响应式布局&#xff0c;提升用户浏览体验。 2&#xff09;增加社区留资组件&#xff0c;更好在社区首页和内容详情页展示 - 包含新建组件&#xff0c;包含…

基于PHP+MySQL医药信息查询系统的设计与开发

医药信息查询系统的基本功能包括用户注册登录,查看医药资讯,医药查询和在线留言等信息。 PHP中药管理系统是一个服务类型的网站,系统通过PHp&#xff1a;MySQL进行开发,分为前台和后台两部分,前台部分主要是让需要买药的人员查看和查询药品信息。后来部分主要是让管理员对网站的…

通过Xshell操作Jetson Nx

1 Jetson Nx Nx留有Uart2 口&#xff0c;便于使用xshell等进行操作。 串口有三根线。链接后使用。 2 XShell 软件 2.1 XShell软件介绍 XShell 软件是一个Windows上运行的终端模拟器&#xff0c;支持SSH, SFTP, TELNET, RLOGIN&#xff0c;和Serial。用于连接Unix或Linux服…

3.7.1、MAC地址(数据链路层)

1、基本介绍 连接在信道上的主机只有它们两个 一个数据链路层地址&#xff1a; 当多个主机连接在同一个广播信道上&#xff0c;要想实现两个主机之间的通信&#xff0c;则每个主机都必须有一个唯一的标识, 在每个主机发送的帧中必须携带标识发送主机和接收主机的地址。由于这…

这支神秘组织,已成功预测了多届世界杯冠军

2022卡塔尔世界杯&#xff0c;正打的如火如荼&#xff0c;也有很多人买球买的不亦乐乎。 现在就有很多朋友、粉丝&#xff0c;通过各种渠道找我&#xff0c;让我来帮他预测一下比赛。可我预测的也不准呀&#xff0c;我都是猜的&#xff0c;不过我不准&#xff0c;不见得别人不准…

m基于基站休眠的LTE-A异构网络中节能算法matlab仿真

目录 1.算法描述 2.仿真效果预览 3.MATLAB核心程序 4.完整MATLAB 1.算法描述 要求 1.开发一个软件工具&#xff0c;可以直观地演示如何在LTE-A异构网络中通过基站的睡眠模式节约能源 2.需要演示基于用户的移动性如何设置基站的开关(睡眠模式) 3.自己设计基站睡眠模式的直观…

Python标准库之pickle

1. pickle标准库简介 pickle&#xff0c;作为名词表示泡菜&#xff0c;作为动词表示用醋或盐水保存食物。由此不难联想到&#xff0c;用存储设备持久化保存数据。而pickle标准库恰是一个 Python 对象结构的二进制序列化和反序列化的核心库&#xff0c;专用于表示Python语言大量…

Stable Diffusion7

它也写到第七部了.. Stability AI宣布&#xff0c;Stable Diffusion 2.0版本上线&#xff01;1.0版本在今年8月出炉&#xff0c;三个月不到&#xff0c;还热乎着呢&#xff0c;新版本就来了。 深度学习文本到图像模型的最新版本——Stable Diffusion 2.0。相较于1.0&#xff…

面试必知的9个性能测试指标,你完全了解吗?

吞吐量 单位时间内&#xff0c;系统能够处理多少请求&#xff0c;吞吐量代表网络的流量&#xff0c;TPS越高&#xff0c;吞吐量越大&#xff0c;还包含了数据的吞吐量。一般单位为秒&#xff0c;每秒处理的请求量。 注意&#xff1a;我们看到的JMeter聚合报告一般如下图&…