C++之多重继承

2023-11-17

大多数应用程序使用单个基类的公用继承,但是在某些情况下,单继承是不够的,必须使用多继承。C++允许为一个派生类指定多个基类,这样的继承结构被称做多重继承.

举个例子,交通工具类可以派生出汽车和船连个子类,但拥有汽车和船共同特性水陆两用汽车就必须继承来自汽车类与船类的共同属性。如下图示:

代码实现:

//多重继承
#include <iostream>
using namespace std;
class Vehicle
{
public:
    Vehicle(int weight = 0)
    {
        Vehicle::weight = weight;
    }
    void SetWeight(int weight)
    {
        cout << "重新设置重量" << endl;
        Vehicle::weight = weight;
    }
    virtual void ShowMe() = 0;//纯虚函数
protected:
    int weight;
private:
};

class car :public Vehicle//汽车
{
public:
    car(int weight = 0, int aird = 0) :Vehicle(weight)
    {
        car::aird = aird;
    }
    void ShowMe()
    {
        cout << "我是汽车!" << endl;
    }
protected:
    int aird;
private:
};

class boat :public Vehicle//船
{
public:
    boat(int weight = 0, float tonnage = 0) :Vehicle(weight)
    {
        boat::tonnage = tonnage;
    }
    void ShowMe()
    {
        cout << "我是船!" << endl;
    }
protected:
    float tonnage;
private:
};

class AmphibianCar :public car, public boat//水陆两用汽车,多重继承的体现
{
public:
    AmphibianCar(int weight, int aird, float tonnage)
        :Vehicle(weight), car(weight, aird), boat(weight, tonnage)
        //多重继承要注意调用基类构造函数
    {

    }
    void ShowMe()
    {
        cout << "我是水陆两用汽车!" << endl;
    }
};

int main()
{
    AmphibianCar a(4, 200, 1.35f);//错误
    a.SetWeight(3);//错误
    system("pause");
}

上面的代码从表面看,看不出有明显的语发错误,但是它是不能够通过编译的。错误如下:
 


这有是为什么呢? 这是由于多重继承带来的继承的模糊性带来的问题。

先看如下的图示:

在图中深红色标记出来的地方正是主要问题所在,水陆两用汽车类继承了来自Car类与Boat类的属性与方法,Car类与Boat类同为AmphibianCar类的基类,在内存分配上AmphibianCar获得了来自两个类的SetWeight()成员函数,当我们调用a.SetWeight(3)的时候计算机不知道如何选择分别属于两个基类的被重复拥有了的类成员函数SetWeight()。

以上面的代码为例,我们要想让AmphibianCar类既获得一个Vehicle的拷贝,而且又同时共享用Car类与Boat类的数据成员与成员函数就必须通过C++所提供的虚拟继承技术来实现。

在Car类和Boat类继承Vehicle类时前面加上virtual关键字就可以实现虚拟继承,使用虚拟继承后,当系统碰到多重继承的时候就会自动先加入一个Vehicle的拷贝,当再次请求一个Vehicle的拷贝的时候就会被忽略,保证继承类成员函数的唯一性。

修改后的代码:

//加virtual后的正确代码
#include <iostream>  
using namespace std;

class Vehicle
{
public:
    Vehicle(int weight = 0)
    {
        Vehicle::weight = weight;
        cout << "载入Vehicle类构造函数" << endl;
    }
    void SetWeight(int weight)
    {
        cout << "重新设置重量" << endl;
        Vehicle::weight = weight;
    }
    virtual void ShowMe() = 0;
protected:
    int weight;
};
class Car :virtual public Vehicle//汽车,这里是虚拟继承  
{
public:
    Car(int weight = 0, int aird = 0) :Vehicle(weight)
    {
        Car::aird = aird;
        cout << "载入Car类构造函数" << endl;
    }
    void ShowMe()
    {
        cout << "我是汽车!" << endl;
    }
protected:
    int aird;
};

class Boat :virtual public Vehicle//船,这里是虚拟继承  
{
public:
    Boat(int weight = 0, float tonnage = 0) :Vehicle(weight)
    {
        Boat::tonnage = tonnage;
        cout << "载入Boat类构造函数" << endl;
    }
    void ShowMe()
    {
        cout << "我是船!" << endl;
    }
protected:
    float tonnage;
};

class AmphibianCar :public Car, public Boat//水陆两用汽车,多重继承的体现  
{
public:
    AmphibianCar(int weight, int aird, float tonnage)
        :Vehicle(weight), Car(weight, aird), Boat(weight, tonnage)
        //多重继承要注意调用基类构造函数  
    {
        cout << "载入AmphibianCar类构造函数" << endl;
    }
    void ShowMe()
    {
        cout << "我是水陆两用汽车!" << endl;
    }
    void ShowMembers()
    {
        cout << "重量:" << weight << "顿," << "空气排量:" << aird << "CC," << "排水量:" << tonnage << "顿" << endl;
    }
};
int main()
{
    AmphibianCar a(4, 200, 1.35f);
    a.ShowMe();
    a.ShowMembers();
    a.SetWeight(3);
    a.ShowMembers();
    system("pause");
}
 

输出:

 

虽然说虚拟继承与虚函数有一定相似的地方,但务必要记住,他们之间是绝对没有任何联系的!

************************************************************************************************************************

************************************************************************************************************************

总结多继承的一些要点和注意事项:

  • 多重继承的情况下,遇到二义性的可能将会更大,编译器不会试图根据派生类转换区别基类间的转换,转换成每个基类都一样好,有如下代码:

class ZooAnimal
{
};

class Bear : public ZooAnimal
{
};

class Endangered
{
};

class Panda : public Bear, public Endangered
{
};

如果有print函数的两个重载版本:

void print(const Bear&);

void print(const Endangered&);

这个调用将会出错,编译器将指出改掉用有二义性。

  • 假定在类的多个基类中定义了相同的成员,将会导致二义性,如果定义了同名的成员函数,即使是函数参数不同,也会导致二义性,代码如下:

class ZooAnimal
{

};

class Bear : public ZooAnimal
{
public:
    void print(int x) { cout << "print Bear" << endl; }

};

class Endangered
{
public:
    void print() { cout << "print Endangered" << endl; };
};

class Panda : public Bear, public Endangered
{
};

int main()
{
    Panda p;
    p.Bear::print(1);  //产生二义  p.print(1);
    p.Endangered::print();
    return 0;
}

输出结果

如果在ZooAnimal中定义了print 而Bear中没有定义,同样会出现二义性。避免二义性的最好方法就是指定函数的作用域,同上示例比如:

Bear::print(x);
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

C++之多重继承 的相关文章

  • C#中unsafe的使用

    1 unsafe在C 程序中的使用场合 实时应用 采用指针来提高性能 引用非 net DLL提供的如C 编写的外部函数 需要指针来传递该函数 调试 用以检测程序在运行过程中的内存使用状况 2 使用unsafe的利弊 好处是 性能和灵活性提高
  • Qt5实现与单片机ATS89S51通信

    Qt实现与单片机直接的通信上位机 单片机代码 测试环境 项目目标 实现效果 关键通信类 QSerialport 总结 这是我大二下学期的单片机课设做的一个小项目 实现上位机与下位机之间的通信 测试环境 开发环境 Qt5 96 Mingw32
  • C++中的namespace

    namespace中文意思是命名空间或者叫名字空间 传统的C 只有一个全局的namespace 但是由于现在的程序的规模越来越大 程序的分工越来越细 全局作用域变得越来越拥挤 每个人都可能使用相同的名字来实现不同的库 于是程序员在合并程序的
  • 范围for语句

    C 新标准提供的范围for语句 这种语句遍历给定序列中个元素并对序列中每一个值执行某种操作 其语法形式是 for declaration expression statement 其中 expression 部分是一个对象 用于表示一个序列
  • 五. python面向对象(多态 和metaclass=abc.ABCMeta)

    一 多态 多肽 一种事物的多种形态 叫多肽 例如 动物 animal 猫 狗 animal py 文件 动物类 都有名字这个属性 和吃这个方法 class Animal object def init self name self name
  • 编写递归算法,计算二叉树叶子结点的数目。

    编写递归算法 计算二叉树叶子结点的数目 编写递归算法 计算二叉树叶子结点的数目 include stdio h 包含 getchar scanf printf include malloc h malloc 动态申请空间 函数 二叉树 结点
  • c/c++入门教程 - 1.基础c/c++ - 1.0 Visual Studio 2019安装环境搭建

    推荐视频课程 https www bilibili com video BV1et411b73Z p 2 已投币三连 b站果然是个学习的网站 本来是想在linux环境下运行QT 于是先学了几个月linux嵌入式驱动开发 后来发现太底层了 与
  • 无法打开源文件<sys/time.h>,但是用time.h编译就会出错,缺少gettimeofday()

    因为sys time h是uinx系统下的库文件 而现在使用的平台是在windows 由于未指明程序运行的系统 导致找不到对应的头文件 需要重新实现gettimeofday 函数 define WIN32 include
  • 【C++】VS code如何配置使用C++(手把手教学)

    博 主 米码收割机 技 能 C Python语言 公众号 测试开发自动化 获取源码 商业合作 荣 誉 阿里云博客专家博主 51CTO技术博主 专 注 专注主流机器人 人工智能等相关领域的开发 测试技术 VS code如何配置使用C 手把手教
  • GDAL多光谱与全色图像融合简单使用

    目录 简述 C 代码 效果对比 GDAL融合效果和原始多光谱波段对比 GDAL融合效果和原始全色波段对比 ARCGIS融合效果与原始全色和多光谱对比 GDAL融合效果与ArcGIS融合效果对比 简述 最近在GDAL的代码中看见了gdalpa
  • C++中的RTTI

    文章目录 dynamic cast运算符 指针类型的dynamic cast 引用类型的dynamic cast typeid运算符 使用RTTI type info类 参考资料 RTTI Runtime Type Information
  • LeetCode题目笔记——17.19消失的两个数字

    文章目录 题目描述 题目难度 困难 方法一 暴力 代码 代码优化 方法二 数学方法 代码 总结 题目描述 题目直达 题目难度 困难 方法一 暴力 虽然题目说你能在 O N 时间内只用 O 1 的空间找到它们吗 但是也没有限制我们不能用暴力
  • 为何在新建STM工程中全局声明两个宏

    在uVision中新建STM32工程后 需要从STM32标准库中拷贝标准外设驱动到自己的工程目录中 此时需要在工程设置 gt C C 选项卡下的Define文本框中键入这两个全局宏定义 STM32F40 41xxx USE STDPERIP
  • R----dplyr包介绍学习

    dplyr包 plyr包的替代者 专门面对数据框 将ddplyr转变为更易用的接口 gt 来自dplyr包的管道函数 其作用是将前一步的结果直接传参给下一步的函数 从而省略了中间的赋值步骤 可以大量减少内存中的对象 节省内存 可惜的是应用范
  • C++:指向类的成员的指针

    引 想必接触过C的朋友们对C语言中指针的概念已经有了深入的了解 如果初步进行了解的朋友可以看一下 C语言基础学习笔记 指针展开来讲的基本知识点包括 指针的概念 指针的定义和初始化及简单使用 指针函数和函数指针 有关指针函数和函数指针的内容上
  • C 语言教程:数据类型和格式说明符

    C 语言中的数据类型 C 中的变量必须是指定的 数据类型 并且您必须在 printf 函数中使用 格式说明符 来显示它 创建变量 int myNum 5 整数 没有小数点 float myFloatNum 5 99 浮点数 char myL
  • C/C++编程中的算法实现技巧与案例分析

    C C 编程语言因其高效 灵活和底层的特性 被广大开发者用于实现各种复杂算法 本文将通过10个具体的算法案例 详细探讨C C 在算法实现中的技巧和应用 一 冒泡排序 Bubble Sort 冒泡排序 Bubble Sort 是一种简单的排序
  • C++ 字符串比较------strcmp函数和strncmp函数

    strcmp 函数原型 int strcmp const char str1 const char str2 功能 strcmp函数会按照字典顺序逐个比较两个字符串的字符 直到遇到不同的字符或者遇到字符串结束符 0 返回值 该函数返回值如下
  • C 语言文件读取全指南:打开、读取、逐行输出

    C 语言中的文件读取 要从文件读取 可以使用 r 模式 FILE fptr 以读取模式打开文件 fptr fopen filename txt r 这将使 filename txt 打开以进行读取 在 C 中读取文件需要一点工作 坚持住 我
  • C++ 中 const 和 constexpr 关键字解析:常量、函数和指针

    很多 C 的初学者看到 const 这个关键字的第一反应都是一头雾水 主要是因为 const 可 以出现在很多的位置 以及后面加入的 constexpr 更是常常感到困惑 今天就为大家一一解释出现它们的含义和以及作用 const 关键字 c

随机推荐

  • 如何快速确定程序的入口

    前言 在阅读代码时 知道程序的入口十分重要 这有助于快速理清程序的逻辑框架 我们找到程序入口后 顺着代码的执行顺序来阅读代码 可以比较容易的理解代码 这里说的代码是编译后成为可执行程序的代码 在linux中就是elf格式 被编译成可执行程序
  • 【学习笔记】模糊控制算法

    本文目录 0 前言 1 概述 2 模糊集合 2 1 集合和论域 2 2 模糊集合的概念 2 3 模糊集合的表示方式 2 4 模糊集合的运算 3 模糊关系与模糊关系合成 3 1 笛卡尔积 3 2 关系与模糊关系 3 3 模糊关系的运算 3 4
  • 自动生成代码的配置generator.properties

    u4EE3 u7801 u751F u6210 u5668 uFF0C u914D u7F6E u4FE1 u606F u5305 u540D package com zscat cms u4F5C u8005 author zscat E
  • python使用scipy.optimize的fsolve求解线性(非线性)方程

    文章目录 求解线性方程 求解非线性方程 求解线性方程 对于固定的线性方程 a 2b 0 4a 5b 0 求 a 与 b 使用如下方法 import scipy optimize as opt import numpy as np def f
  • 【JDK版本常见报错及其解决,Java基础知识点百度云

    J2SE 8 52 J2SE 7 jdk1 7 51 J2SE 6 0 jdk1 6 50 J2SE 5 0 jdk1 5 49 JDK 1 4 48 JDK 1 3 47 JDK 1 2 46 JDK 1 1 45 那怎么办 好办 把项目
  • 【链表】环型链表找环的起点

    力扣 142 环型链表 II 题目 给定一个链表的头节点 head 返回链表开始入环的第一个节点 如果链表无环 则返回 null 1 先确定链表存在环 使用快慢指针法 分别定义 fast 和 slow 指针 从头结点出发 fast 指针每次
  • FFmpeg中的常见结构体

    代码基于FFmpeg5 0 1 目录 FFFormatContext AVFormatContext AVIOContext FFIOContext URLContext URLProtocol AVInputFormat FFStream
  • PAT甲级刷题:模拟(不断更新)

    目录 1001 A B Format 1005 Spell It Right 1035 Password 1061 Dating 18 20 1073 Scientific Notation 16 20 1077 Kuchiguse 17
  • Java 华为真题-猴子爬山

    需求 一天一只顽猴想去从山脚爬到山顶 途中经过一个有个N个台阶的阶梯 但是这猴子有一个习惯 每一次只能跳1步或跳3步 试问猴子通过这个阶梯有多少种不同的跳跃方式 输入描述 输入只有一个整数N 0
  • k8s中各组件和kube apiserver通信时的认证和鉴权

    背景 和master节点kube api server通信的组件有很多 包括 kubelet calico scheduler kubectl 某些pod可能会和kube api server通信 这些组件和api server通信时用的是
  • Springboot使用EasyExcel读写excel(详细)

    文章目录 使用EasyExcel读取Excel 一 关于EasyExcel 二 读取excel 实体类 本地读取 controller上传 创建监听器 三 导出excel 实体类 本地导出 Controller下载 使用EasyExcel读
  • 模糊pid控制的温度系统matlab源代码_模糊PID控制系统(一)模糊入门

    1 matlab模糊工具箱 1 1 matlab命令 fuzzy 打开fuzzy设计工具箱 1 2 添加输入输出 隶属度函数 数值范围 1 3 确定模糊规则表 添加模糊规则 也可在matlab 编辑器里编辑 fis文件 1 4 反模糊化 1
  • HTTP协议之multipart/form-data请求分析

    无意中发现了一个巨牛的人工智能教程 忍不住分享一下给大家 教程不仅是零基础 通俗易懂 而且非常风趣幽默 像看小说一样 觉得太牛了 所以分享给大家 点这里可以跳转到教程 首先来了解什么是multipart form data请求 根据http
  • hook-setInterval定时器

    目标网站 aHR0cDovL3NwaWRlci53YW5nbHVvemhlLmNvbS9jaGFsbGVuZ2UvNQ 背景 本题为hook初体验 目标是用hook的方式过掉定时器 成功在控制台中打印出我们想要的内容 表现形式 1 一进入页
  • 【Java愚公】Windows安装wsl2

    Windows安装wsl2 查看window有没有安装wsl 在window下安装wsl 查看window有没有安装wsl 在cmd命令面板输入 wsl 没有安装wsl输入会报错 在window下安装wsl 通过在管理员PowerShell
  • js实现图片上下滚动background-position

  • 关于js报错 Cannot read property innerHTML of null和关于Cannot set property ‘innerHTML’ of null 错误原因

    解决关于js报错 Cannot read property innerHTML of null 1 相信很多同学在开发过程中都会遇到 Cannot read property innerHTML of null 这个报错的字面含义是 不能读
  • 前端文件下载的八种方法(解决pdf、图片在浏览器自动打开问题)

    系列文章目录 现在流行的chrom 和火狐浏览器 都会将图片和文档自动打开 图片自动打开的问题已经解决 请看第三条 提示 下面代码中会用到a标签中 target 会添加一个 view window 的属性 如果想要详细了解a标签的属性 可以
  • rabbitMQ初识

    消息队列 RabbitMQ 认识MQ 同步和异步通讯 微服务间通讯有同步和异步两种方式 同步通讯 就像打电话 需要实时响应 异步通讯 就像发邮件 不需要马上回复 同步通讯 同步调用的优点 时效性较强 可以立即得到结果 同步调用的问题 耦合度
  • C++之多重继承

    大多数应用程序使用单个基类的公用继承 但是在某些情况下 单继承是不够的 必须使用多继承 C 允许为一个派生类指定多个基类 这样的继承结构被称做多重继承 举个例子 交通工具类可以派生出汽车和船连个子类 但拥有汽车和船共同特性水陆两用汽车就必须