多重继承 与 虚基类

24、C++多重继承:菱形继承与虚基类的深入探讨 本文深入探讨了C++中的多重继承,特别是菱形继承带来的问题及其解决方案——虚基类的使用。通过具体示例分析了菱形继承导致的内存浪费、构造析构开销和访问歧义问题,并详细介绍了虚基类的实现机制、使用限制及优化方法。文章还提供了完整的代码示例,并讨论了虚基类在实际应用场景中的权衡与决策流程,帮助开发者更好地设计类继承结构,提高代码质量与可维护性。 阅读详情

使用多个基类的继承被称为多重继承(multiple inheritance,MI)

私有MI和保护MI表示has-a关系。

 

公有多重继承
    与单继承一样,公有MI表示的也是is-a关系。必须使用关键字public来限定每个基类。这是因为,除非特别指出,否则编译器将认为是私有派生:
class AB:public B,A {...}; // A is a private base

 

    假设定义一个抽象基类worker,并使用它派生出waiter类和singer类。然后便可以使用MI从waiter类和singer类派生出SingingWaiter类,这将导致MI的大多数麻烦。

 

从waiter类和singer类派生出SingingWaiter类:
class singingwaiter: public singer,public waiter{...};

 

1.有多少个worker
    因为waiter和singer都继承了worker组件,因此SingingWaiter将包含两个worker组件。
   

    通常可以将派生类对象的地址赋给基类基址,但是现在将出现二义性:
SingingWaiter ed;
worker * pw = &ed;
// ambiguous

   

    通常,这种赋值将把基类指针设置为派生对象中的基类对象的地址。但是ed中包含两个worker对象,有两个地址可供选择,所以应使用类型转换来制定对象:
worker *pw1=(waiter *) &ed; // the worker in waiter
worker *pw2=(singer *) &ed; // the worker in singer
这将使得使用基类指针来引用不同的对象(多态性)复杂化。

 

解决办法:虚基类(virtual base class)

 

虚基类使得从多个类(它们的基类相同)派生出的对象只继承一个基类对象。

通过在类声明中使用关键字virtual,可以使worker被用作singer和waiter的虚基类(virtual和public的次序无关紧要):
class singer: virtual public worker {...};
class waiter: public virtual worker {...};

 

然后,可以将singingwaiter类定义为:
class singingwaiter: public singer,public waiter {...};

   

     现在singingwaiter对象将只包含worker对象的一个拷贝。从本质上说,继承的singer和waiter对象共享一个worker对象,而不是各自引入自己的worker对象拷贝。

 

使用虚基类时的构造函数
   
    假设使用非虚基类。类A派生类B,类B再派生类C,当使用构造函数时,信息将自动传递:

class C: public B
{int c;
 public: C(int q=0,int m=0,int n=0):B(m,n){c=q;}
 ...
};

 

   这里,C类的构造函数使用值q,并将值m和n传递给B类的构造函数;而B类的构造函数使用值m,并将值n传递给A类的构造函数。

 

如果worker是虚基类,则这种信息自动传递将不起作用。例如:
singingwaiter(const worker & wk,int p=0,int v=singer::other)
             :waiter(wk,p),singer(wk,v){}
  // flawed

 

   存在的问题是,自动传递消息时,将通过2条不同的途径(waiter和singer)将wk传递给worker对象。为避免这种冲突,在基类是虚拟的时,禁止信息通过中间类自动传递给基类。因此,上述构造函数里wk参数中的信息将不会传递给子对象waiter和singer。编译器将使用worker的默认构造函数。

 

    如果类有间接虚基类,则除非只需使用该虚基类的默认构造函数,否则必须显示地调用该虚基类的某个构造函数。

 

singingwaiter(const worker & wk,int p=0,int v=singer::other)
             :worker(wk),waiter(wk,p),singer(wk,v){}

上述代码将显示地调用构造函数worker(const worker &)。
这种用法对于虚基类是必须的;但对于非虚基类则是非法的。

 

2.哪个方法
    假设waiter和singer中定义了方法show()。在singingwaiter类中并未重新定义show()方法,又试图使用singingwaiter对象调用继承的show()方法:
singingwaiter nawhire("elise hawks",2005,6,soprano);
nawhire.show();
  // ambiguous

 

    对于单继承,如果没有重新定义show(),则将使用最近祖先中的定义。而在MI中,每个直接祖先都有一个show()函数,这使得上述调用存在二义性。

 

可以使用作用域解析操作符来澄清编程者的意图:
singingwaiter nawhire("elise hawks",2005,6,soprano);
nawhire.singer::show();
   // use singer version

 

    不过更好的方法是在singingwaiter中重新定义show(),并指出要使用哪个show()。

 

    在祖先相同时,使用MI必须引入虚基类,并修改构造函数初始化列表的规则。另外,如果在编写这些类时没有考虑到MI,则还可能需要重新编写它们。

 

混合使用虚基类和非虚基类


    假设类B被用作类C和D的虚基类,同时被用作类X和Y的非虚基类,而类M是从C、D、X和Y派生而来的。在这种情况下,类M从虚拟派生祖先(C和D)那里共继承了一个B类子对象,并从每一个非虚拟派生祖先(X和Y)分别继承了一个B类子对象。因此,它包含三个B类子对象。
    当类通过多条虚拟途径和非虚拟途径继承某个特定的基类时,该类将包含一个表示所有的虚拟途径的基类子对象和分别表示各条非虚拟途径的多个基类子对象。

 

虚基类和支配
    使用虚基类将改变C++解析二义性的方式。使用非虚基类时,规则很简单。如果类从不同的类那里继承了两个或更多的同名成员(数据或方法),则使用该成员名时,如果没有用类名进行限定,将导致二义性。但如果使用的是虚基类,则这样做不一定会导致二义性。
    在这种情况下,如果某个名称优先于(dominates)其他所有名称,则使用它时,即便不是用限定符,也不会导致二义性。

 

成员名如何优先:
(派生类中的名称优先于直接或间接祖先类中的相同名称)
class B
{public:short q();
 ...};

class C: virtual public B
{public: long q();int omb();
 ...};

class D: public C
{ ... };

class E: virtual public B
{private:int omb();
 ...};

class F: public D,public E
{ ... };

 

    类C中的q()定义优先于类B中的q()定义,因为类C是从类B派生而来的。因此F中的方法可以使用q()来表示C::q()。而任何一个omb()定义都不优先于其他omb()定义,因为C和E都不是对方的基类。所以,在F中使用非限定的omb()将导致二义性。
    虚拟二义性规则与访问规则无关,也就是说,即使E::omb()是私有的,也不能在F类中直接访问,但使用omb()仍将导致二义性。同样,即使C::q()是私有的,它也将优先于B::q()。在这种情况下,可以在类F中调用B::q(),但如果不限定q(),则意味着要调用不可访问的C::q()。

C++多重继承与虚基类的深入探讨 本文深入探讨了C++中的多重继承及其相关问题,特别是虚拟基类的使用,以及在多重继承中可能出现的常见编程错误。通过具体案例分析,讨论了如何在多重继承中解决成员变量冲突,以及如何正确使用继承以优化类的设计。 阅读详情

相关推荐

FPGA+滨淞CCD实战:手把手教你搭建S7031/S10142成像电路(附避坑指南)

本文详细介绍了使用FPGA驱动滨淞CCD传感器(S7031/S10142)构建高精度成像系统的完整实战流程。内容涵盖核心器件选型、系统架构规划、电路板(焦面板与接口板)设计要点、FPGA Verilog逻辑实现,并提供了从电源管理、信号调理到数据采集的详细避坑指南,帮助读者从零搭建稳定可靠的成像电路。

weixin_29271263的博客 227

C++ PrimerPlus 学习笔记(三)类与对象

文章目录

苹果好吃呀的博客 304

从零到一:R与RStudio的安装与配置全攻略

本文提供了R与RStudio的完整安装与配置指南,涵盖Windows、Mac和Linux三大操作系统。从基础环境搭建到高级性能优化,详细介绍了R语言安装、RStudio配置、项目管理和实用技巧,帮助数据科学初学者快速搭建高效的分析工作环境。

weixin_29062671的博客 419

C++多继承

转载自:https://www.cnblogs.com/shijingjing07/p/5564812.html#top 在C++语言中,一个派生类可以从一个基类派生,也可以从多个基类派生。从一个基类派生的继承称为单继承;从多个基类派生的继承称为多继承。 1.继承的三种方式 公有继承(public),私有继承(private),保护继承(protected) 三种继承方式的说明,如下表所示: 2...

mayue_web的博客 4762

Docker Desktop 在 Windows 上的安装和使用

WSL 指 “”,这是一个在 Windows 10\11 上能够运行原生 Linux 二进制可执行文件(ELF格式)的兼容层。它是由微软与Canonical公司合作开发的,旨在使纯正的Ubuntu、Debian等映像能下载和解压到用户的本地计算机,并且映像内的工具和实用工具能在此子系统上原生运行。这极大地拓展了Windows系统的功能,使用户能够在Windows环境中享受到Linux的强大功能。//Linux容器。

swadian2008的博客 6万+

泰凌微8258入门教程 基础篇①——Bluetooth® SIG Mesh 快速上手

Bluetooth® SIG Mesh Quick Start

6729

构造函数的一些需要注意的特性(一)

1.如果一个类,你没有定义构造函数,那么系统默认会有一个无参的构造函数但如果你定义了一个有参的构造函数,为了保证正确性,系统不会创建无参构造函数,这时候,如果你还想允许无参构造,就必须显式的声明一个。 #include"stdafx.h" #include<iostream> #include<stdlib.h> using namespace std; class B0...

l93919861的博客 1252

银行业数据科学家的 6 条经验教训

原本希望在计算机科学、统计学和机器学习领域工作。运用新方法获得独特见解,实现一切自动化。简而言之,最终成了这个职业炒作的牺牲品。我想和你们分享一下。希望我们能够摆脱炒作,提高你对数据科学家工作的理解。

茶桁专栏 246

多态

前面讲了父类指针指向子类对象时,可以访问子类的成员变量(从父类继承过来的成员变量),但是成员函数(从父类继承过来的成员函数)时,依旧访问的是父类的成员函数。 (我们说这是因为,成员函数并没有存如到对象的内存中,而是在全局数据区)。 为了解决这个问题,c++增加了虚函数。注意是虚函数不是虚继承 虚继承是在出现菱形继承时,在继承方式前面加virtual,保证父类成员只出现一次 在初始化时调用间接基类进...

不积极分子 321

论文必备 - 绘制YOLOv8模型在训练过程中,精准率,召回率,mAP_0.5,mAP_0.5:0.95,以及各种损失的变化曲线

文件是模型在训练过程中,实时记录训练的结果,和当时控制台输出的信息一致。当然还包含一些验证损失,学习率变化等数据。epoch:记录训练的轮次,总轮次为训练文件中设置的epochs大小。:边界框损失。在训练过程中,此值逐渐变小,越小表示模型预测的边界框与真实边界框之间的差异越小。:分类损失。在训练过程中,此值逐渐变小,越小表示模型对目标类别的预测越准确。:分布焦点损失。将边界框的回归问题转化为对边界框四个边的分布预测问题,越小表示预测越准确。:精确率也称为查准率。

Limiiiing的博客 1406

瑞芯微RV1126部署YOLOv8全流程:环境搭建、pt-onnx-rknn模型转换、C++推理代码、错误解决、优化、交叉编译第三方库

瑞芯微RV1126部署YOLOv8全流程:环境搭建、pt-onnx-rknn模型转换、C++推理代码、错误解决、优化、交叉编译第三方库

C/C++、嵌入式开发、深度学习算法、模型部署与推理优化 3106

继承:复杂的菱形继承与虚继承

继承:复杂的菱形继承与虚继承(CPP)

2302_80190394的博客 1637

【软考中级网络工程师】知识点之常用网络诊断和配置命令

本文聚焦软考中级网络工程师必备的网络诊断和配置命令,详细解析 ipconfig、ping、arp 等 11 类核心命令。涵盖各命令的功能、语法、常用参数及应用场景,如 ipconfig 查看配置、ping 测试连通性等。还通过图呈现命令执行逻辑与流程,助读者理解原理。最后结合软考考点回顾重点并提供真题练习,为备考和实际运维提供全面指导,是掌握网络命令的实用参考。

邓邓子的博客 1541

C++ 多重继承与虚基类

技术交流qq:1032492413 技术交流VX:Chamfion

偶然i的博客 1093

WINCC7.3破解文件

WINCC7.3硬件够破解文件,有需要的下载!WINCC7.3硬件够破解文件,有需要的下载!

TMS_WebGMaps_1.9.8.0_Full_Source

TMS_WebGMaps_1.9.8.0_Full_Source 全源码 delphi cbuilder

C++类继承基础6——多重继承与虚基类

虚基类是在派生类定义时,指定继承方式时声明的。class 派生类名: virtual 访问标签 虚基类名,... { //类体成员列表还有一种形式是class 派生类名: 访问标签 virtual 虚基类名,... {// 成员列表需要注意,为了保证虚基类在派生类中只继承一次,应当在该基类的所有直接派生类中声明为虚基类。否则仍然会出现对基类的多次继承(我们下面会举例子)例:虚基类举例class A { //声明为基类Apublic:A(int n) { //A类的构造函数nv=n;

2301_80224556的博客 1910

汉书HE8,好用的汇编代码编写工具

汉书HE8,好用的汇编代码编写工具早期编写汇编代码所用功能强大

多重继承、虚继承与虚基类

一、多重继承 单重继承——一个派生类最多只能有一个基类 多重继承——一个派生类可以有多个基类 class 类名: 继承方式 基类1,继承方式 基类2,…. {….}; 派生类同时继承多个基类的成员,更好的软件重用 可能会有大量的二义性,多个基类中可能包含同名变量或函数 多重继承中解决访问歧义的方法: 基类名::数据成员名(或成员函数(参数表)) 明确指明要访问定义于哪个基类中的成员 #includ...

天行九歌 409

jdk-8u144-linux-x64.tar.gz.

该资源为搭建Linux系统Java环境用,资源的后缀为tar.gz格式的压缩包

多重继承与虚基类

MI描述的是有多个直接基类的类。 注意:公有继承必须使用关键字public限定每一个基类,因为默认为private。 class SingWaiter : public Waiter, public Singer {...}; MI面临的问题: 从两个不同的基类继承同名的方法; 从两个或更多相关基类那里继承同一个类的多个实例。 菱形继承问题: 当多重继承的两个父类有共同的祖先时,...

Jibeon的学习之路 390

C++ 多重继承、虚继承与虚基类

前言:虚继承是为了解决多重继承出现菱形继承时出现的问题。例如:类B、C分别继承了类A。类D多重继承类B和C的时候,类A中的数据就会在类D中存在多份。通过声明继承关系的时候加上virtual关键字可以实现虚继承。 一、多重继承 单重继承——一个派生类最多只能有一个基类 多重继承——一个派生类可以有多个基类 class 类名: 继承方式 基类1,继承方式 基类2,…. {….}; 派生类同...

Magnum的博客 3907

C++多重继承,虚基类与友元

详细介绍了C++的多重继承以及相应的二义性问题的处理方式,虚基类的声明与定义,友元的声明与定义!

cx330的博客 1349

继承与派生(Part Ⅲ)——多重继承 & 虚基类

一个类不仅可以派生出一个派生类,派生类还可以继续派生,形成派生的层次结构。

Miss Z的博客 1356

虚基类与多重继承

在说明虚基类之前,不得不提多重继承(MI)的概念。C++是允许多重继承的存在的,但是多重继承会导致以下两个问题: 从两个不同基类继承 同名方法 从两个或更多相关基类那里继承同一个类的多个实例  对于二者都可以使用作用域运算法来限定要访问的类名,以此解决二义性。对于第二个问题,可以使用虚基类来解决。虚基类使得从多个类(它们的基类相同)派生出的对象只继承一个基类对象。虚基类使用方法://Worker被用

huanghuihuang1991的专栏 428

C++多重继承与虚基类

C++多重继承与虚基类 1.多重继承 多重继承就是一个派生类继承了多个基类。 I>初始化 i>通过成员初始化列表指定构造函数 class D : public B, public C { private: int a; public: D(int a1, int a2, int a3) : B(a1),C(a2),a(a3) { cout &...

qq2071114140的博客 479

C++的多重继承与虚基类

多重继承(MI) 多重继承是指使用多个基类的继承,可以分为私有MI,保护MI,公有MI。这里要特别强调一下公有MI,因为它将导致一些问题。

ajt1234的博客 285

C++继承之多重继承与虚基类

C++存在多重继承,允许一个派生类同时继承多个基类。语法如下: class D:public A,private B,proected C {     }; 多重继承容易产生几个问题: 一是由于继承的成员同名产生的二义性问题。比如类A,类B都有成员a,当类C继承了类A,类B时,在使用a成员时,会出现二义性,不知道应该引用A类的还是B类的,解决方法是指定类: C c1; c1.A::a

xckkcxxck的博客 1077

C++继承、多重继承与虚基类 继承、多重继承时对象内存分布

类对象内存模型 类是对属性和行为的封装,在类的对象中也有属性(成员变量)和行为(成员函数),反映到类对象的内存模型中,也就是内存中应该有对象的成员变量和成员函数。在同一个类的所有对象的成员函数,并不是每个对象有一份成员函数,而是共有一份相同的成员函数。例如下面一个类: [cpp] view plaincopyprint? class Base { //行为 public:

xiaoyegui的专栏 1286
上一篇: using重新定义访问权限
下一篇: 类模板详解
TypeCool
博客等级 码龄22年 58粉丝 93原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值