[转载]虚继承与虚基类的本质

别再死记硬背了!用51单片机的AD/DA和PWM,亲手做个简易示波器信号发生器 本文详细介绍了如何利用51单片机的PWM和AD/DA功能制作简易示波器信号发生器。通过PWM模拟DAC输出波形,结合XPT2046 ADC进行信号测量,实现方波、三角波和正弦波的生成与显示。项目不仅成本低廉,还能帮助电子爱好者深入理解51单片机的核心功能,是理论与实践结合的绝佳案例。 阅读详情

      虚继承和虚基类的定义是非常的简单的,同时也是非常容易判断一个继承是否是虚继承的,虽然这两个概念的定义是非常的简单明确的,但是在C++语言中虚继承作为一个比较生僻的但是又是绝对必要的组成部份而存在着,并且其行为和模型均表现出和一般的继承体系之间的巨大的差异(包括访问性能上的差异),现在我们就来彻底的从语言、模型、性能和应用等多个方面对虚继承和虚基类进行研究。
      首先还是先给出虚继承和虚基类的定义。
      虚继承:在继承定义中包含了virtual关键字的继承关系;
      虚基类:在虚继承体系中的通过virtual继承而来的基类,需要注意的是:
                  struct CSubClass : public virtual CBase {}; 其中CBase称之为CSubClass的虚

                  基类,而不是说CBase就是个虚基类,因为CBase还可以不不是虚继承体系中的基类。
      有了上面的定义后,就可以开始虚继承和虚基类的本质研究了,下面按照语法、语义、模型、性能和应用五个方面进行全面的描述。

      1. 语法
      语法有语言的本身的定义所决定,总体上来说非常的简单,如下:
           struct CSubClass : public virtual CBaseClass {};
      其中可以采用public、protected、private三种不同的继承关键字进行修饰,只要确保包含virtual就可以了,这样一来就形成了虚继承体系,同时CBaseClass就成为了CSubClass的虚基类了。
       其实并没有那么的简单,如果出现虚继承体系的进一步继承会出现什么样的状况呢?
       如下所示:
            /*
             * 带有数据成员的基类
             */
            struct CBaseClass1
            {
                CBaseClass1( size_t i ) : m_val( i ) {}
            
                size_t m_val;
            };
            /*
             * 虚拟继承体系
             */
            struct CSubClassV1 : public virtual CBaseClass1
            {
                CSubClassV1( size_t i ) : CBaseClass1( i ) {}
            };           
            struct CSubClassV2 : public virtual CBaseClass1
            {
                CSubClassV2( size_t i ) : CBaseClass1( i ) {}
            };           
            struct CDiamondClass1 : public CSubClassV1, public CSubClassV2
            {
                CDiamondClass1( size_t i ) : CBaseClass1( i ), CSubClassV1( i ), CSubClassV2( i ) {}
            };           
            struct CDiamondSubClass1 : public CDiamondClass1
            {
                CDiamondSubClass1( size_t i ) : CBaseClass1( i ), CDiamondClass1( i ) {}
            };
       注意上面代码中的CDiamondClass1和CDiamondSubClass1两个类的构造函数初始化列表中的内容。可以发现其中均包含了虚基类CBaseClass1的初始化工作,如果没有这个初始化语句就会导致编译时错误,为什么会这样呢?一般情况下不是只要在CSubClassV1和CSubClassV2中包含初始化就可以了么?要解释该问题必须要明白虚继承的语义特征,所以参看下面语义部分的解释。
      
    2. 语义
       从语义上来讲什么是虚继承和虚基类呢?上面仅仅是从如何在C++语言中书写合法的虚继承类定义而已。首先来了解一下virtual这个关键字在C++中的公共含义,在C++语言中仅仅有两个地方可以使用virtual这个关键字,一个就是类成员虚函数和这里所讨论的虚继承。不要看这两种应用场合好像没什么关系,其实他们在背景语义上具有virtual这个词所代表的共同的含义,所以才会在这两种场合使用相同的关键字。那么virtual这个词的含义是什么呢?
       virtual在《美国传统词典[双解]》中是这样定义的:
           adj.(形容词)
           1. Existing or resulting in essence or effect though not in actual
              fact, form, or name:
              实质上的,实际上的:虽然没有实际的事实、形式或名义,但在实际上或效
              果上存在或产生的;
           2. Existing in the mind, especially as a product of the imagination.
              Used in literary criticism of text.
              虚的,内心的:在头脑中存在的,尤指意想的产物。用于文学批评中。
       我们采用第一个定义,也就是说被virtual所修饰的事物或现象在本质上是存在的,但是没有直观的形式表现,无法直接描述或定义,需要通过其他的间接方式或手段才能够体现出其实际上的效果。那么在C++中就是采用了这个词意,不可以在语言模型中直接调用或体现的,但是确实是存在可以被间接的方式进行调用或体现的。比如:虚函数必须要通过一种间接的运行时(而不是编译时)机制才能够激活(调用)的函数,而虚继承也是必须在运行时才能够进行定位访问的一种体制。存在,但间接。其中关键就在于存在、间接和共享这三种特征。

       对于虚函数而言,这三个特征是很好理解的,间接性表明了他必须在运行时根据实际的对象来完成函数寻址,共享性表象在基类会共享被子类重载后的虚函数,其实指向相同的函数入口。
       对于虚继承而言,这三个特征如何理解呢?存在即表示虚继承体系和虚基类确实存在,间接性表明了在访问虚基类的成员时同样也必须通过某种间接机制来完成(下面模型中会讲到),共享性表象在虚基类会在虚继承体系中被共享,而不会出现多份拷贝。那现在可以解释语法小节中留下来的那个问题了,“为什么一旦出现了虚基类,就必须在没有一个继承类中都必须包含虚基类的初始化语句”。由上面的分析可以知道,虚基类是被共享的,也就是在继承体系中无论被继承多少次,对象内存模型中均只会出现一个虚基类的子对象(这和多继承是完全不同的),这样一来既然是共享的那么每一个子类都不会独占,但是总还是必须要有一个类来完成基类的初始化过程(因为所有的对象都必须被初始化,哪怕是默认的),同时还不能够重复进行初始化,那到底谁应该负责完成初始化呢?C++标准中(也是很自然的)选择在每一次继承子类中都必须书写初始化语句(因为每一次继承子类可能都会用来定义对象),而在最下层继承子类中实际执行初始化过程。所以上面在每一个继承类中都要书写初始化语句,但是在创建对象时,而仅仅会在创建对象用的类构造函数中实际的执行初始化语句,其他的初始化语句都会被压制不调用。
      
    3. 模型
       为了实现上面所说的三种语义含义,在考虑对象的实现模型(也就是内存模型)时就很自然了。在C++中对象实际上就是一个连续的地址空间的语义代表,我们来分析虚继承下的内存模型。
       3.1. 存在
           也就是说在对象内存中必须要包含虚基类的完整子对象,以便能够完成通过地址完成对象的标识。那么至于虚基类的子对象会存放在对象的那个位置(头、中间、尾部)则由各个编译器选择,没有差别。(在VC8中无论虚基类被声明在什么位置,虚基类的子对象都会被放置在对象内存的尾部)
       3.2. 间接
           间接性表明了在直接虚基承子类中一定包含了某种指针(偏移或表格)来完成通过子类访问虚基类子对象(或成员)的间接手段(因为虚基类子对象是共享的,没有确定关系),至于采用何种手段由编译器选择。(在VC8中在子类中放置了一个虚基类指针vbc,该指针指向虚函数表中的一个slot,该slot中存放则虚类子对象的偏移量的负值,实际上就是个以补码表示的int类型的值,在计算虚基类子对象首地址时,需要将该偏移量取绝对值相加,这个主要是为了和虚表中只能存放虚函数地址这一要求相区别,因为地址是原码表示的无符号int类型的值)。
       3.3. 共享
           共享表明了在对象的内存空间中仅仅能够包含一份虚基类的子对象,并且通过某种间接的机制来完成共享的引用关系。在介绍完整个内容后会附上测试代码,体现这些内容。
    4. 性能
       由于有了间接性和共享性两个特征,所以决定了虚继承体系下的对象在访问时必然会在时间和空间上与一般情况有较大不同。
       4.1. 时间
           在通过继承类对象访问虚基类对象中的成员(包括数据成员和函数成员)时,都必须通过某种间接引用来完成,这样会增加引用寻址时间(就和虚函数一样),其实就是调整this指针以指向虚基类对象,只不过这个调整是运行时间接完成的。(在VC8中通过打开汇编输出,可以查看*.cod文件中的内容,在访问虚基类对象成员时会形成三条mov间接寻址语句,而在访问一般继承类对象时仅仅只有一条mov常量直接寻址语句)
       4.2. 空间
           由于共享所以不同在对象内存中保存多份虚基类子对象的拷贝,这样较之多继承节省空间。
    5. 应用
       谈了那么多语言特性和内容,那么在什么情况下需要使用虚继承,而一般应该如何使用呢?
       这个问题其实很难有答案,一般情况下如果你确性出现多继承没有必要,必须要共享基类子对象的时候可以考虑采用虚继承关系(C++标准ios体系就是这样的)。由于每一个继承类都必须包含初始化语句而又仅仅只在最底层子类中调用,这样可能就会使得某些上层子类得到的虚基类子对象的状态不是自己所期望的(因为自己的初始化语句被压制了),所以一般建议不要在虚基类中包含任何数据成员(不要有状态),只可以作为接口类来提供。

文章转自:http://zzg810314.javaeye.com/blog/134075 

Android性能优化之Android Profiler 在移动应用开发中,打造一个流畅、省电且不卡顿的用户体验是至关重要的。然而,随着应用功能的日益复杂,内存泄漏、CPU暴涨、网络卡顿以及电量消耗过快等问题常常化身为隐形杀手,悄悄吞噬着用户体验。为了帮助开发者精准定位并解决这些性能瓶颈,谷歌在Android Studio中集成了一款强大的一站式性能分析工具Android Profiler。Android Profiler作为Android Studio内置的实时数据可视化工具,支持运行在Android 5.0及更高版本的设备上。 阅读详情

相关推荐

BLDC方波控制双闭环Simulink仿真模型

无刷直流电机方波控制双闭环Simulink仿真模型搭建详细教程,包括速度PI环、电流PI环、换向逻辑和受控电压源,仿真结果良好。

m0_57458432的博客 6867

C++虚继承和虚基类

C++虚继承和虚基类 本文为转载C语言中文网内容 原文链接:http://c.biancheng.net/view/2280.html 多继承(Multiple Inheritance)是指从多个直接基类中产生派生类的能力,多继承的派生类继承了所有父类的成员。尽管概念上非常简单,但是多个基类的相互交织可能会带来错综复杂的设计问题,命名冲突就是不可回避的一个。 多继承时很容易产生命名冲突,即使我...

hhb442的博客 212

SPARTAN6 + Verilog - FPGA step by step 从0开始学习(1)

SPARTAN6 + Verilog - FPGA step by step 从0开始学习(1)Step 1 软件安装Step 2 硬件获取Step 3 开始从头到脚建一个工程Step 3.1 新建一个工程 从0开始学习FPGA,最好的办法就是先建一个最简单的模块,并且跑起来,然后再慢慢展开。下面这个例子是要按照一定的周期点亮核心板上的一个led。 Step 1 软件安装 这个从0开始学习是用的xilinx的SPARTAN6。SPARTAN6集成开发环境用ISE,不能用Vivado,各自对应的版本不同。安装

xfacexx点点记录 5507

转载:C++虚继承的实现原理

原文链接:http://blog.youkuaiyun.com/xiejingfa/article/details/48028491 准备工作 1、VS2012使用命令行选项查看对象的内存布局 微软的Visual Studio提供给用户显示C++对象在内存中的布局的选项:/d1reportSingleClassLayout。使用方法很简单,直接在[工具(T)]选项下找到“Visual Studio命令提示(C)”...

se7eN0x的博客 373

N32串口DMA接收不定长数据:空闲中断与DMA通道的协同处理

本文详细解析了在N32系列MCU中,利用串口空闲中断与DMA通道协同处理不定长数据的核心机制与实战方法。通过剖析空闲中断的触发时机、DMA的配置要点以及两者协同工作的关键时序,提供了避免数据错乱的避坑指南和完整的代码实现,旨在帮助开发者构建高效、可靠的嵌入式串口通信子系统。

weixin_29191669的博客 122

Qt Installer Framework4.6.1的最基础使用1(win10+离线安装包 )

因工作需要,win程序发布后,打包成一可执行exe文件,且兼容win7系统笔者记录了一些关键要点,供有需要的小伙伴使用。

aggs1990的专栏 904

【C++拾遗】 从内存布局看C++虚继承的实现原理

准备工作 1、VS2012使用命令行选项查看对象的内存布局 微软的Visual Studio提供给用户显示C++对象在内存中的布局的选项:/d1reportSingleClassLayout。使用方法很简单,直接在[工具(T)]选项下找到“Visual Studio命令提示(C)”后点击即可。切换到cpp文件所在目录下输入如下的命令即可       c1 [filename].cpp

上善若水,人淡如菊 2万+

黑猫带你学UFS协议第13篇:UFS初始化详解(Boot Operation)

本文详细解析了UFS设备的初始化过程,包括部分初始化、加载启动代码(可选)和初始化完成三个阶段。重点介绍了如何通过设备描述符和逻辑单元描述符配置引导特性,以及如何从UFS设备加载启动代码。内容适用于嵌入式硬件和存储领域的开发者。

黑猫奥利奥的博客 3554

C++虚继承

虚继承 虚继承用于解决多继承条件下的菱形继承问题(浪费存储空间、存在二义性)。 底层实现原理与编译器相关,一般通过虚基类指针和虚基类表实现,每个虚继承的子类都有一个虚基类指针(占用一个指针的存储空间,4字节)和虚基类表(不占用类对象的存储空间)(需要强调的是,虚基类依旧会在子类里面存在拷贝,只是仅仅最多存在一份而已,并不是不在子类里面了);当虚继承的子类被当做父类继承时,虚基类指针也会被继承。...

weixin_30760895的博客 108

网络安全 | 企业网络安全的十大挑战及解决方案

网络安全 | 企业网络安全的十大挑战及解决方案,随着信息技术的飞速发展,企业数字化转型进程不断加速,网络安全问题日益成为企业运营与发展面临的关键挑战。本文深入剖析企业网络安全的十大挑战,包括网络攻击日益复杂、数据泄露风险、内部威胁、物联网安全、云安全、合规性要求、移动办公安全、供应链安全、人才短缺以及安全预算有限等,并针对每个挑战详细阐述相应的解决方案,旨在为企业构建全面、有效的网络安全体系提供指导与参考,助力企业在复杂多变的网络环境中保障自身信息资产安全,实现可持续发展。

不踩坑,不知道坑有多深;不总结,踩过的坑白踩。 4万+

Python 爬虫实战:医学临床指南爬取(含 PDF 解析与结构化存储)

在当今数字化时代,医学临床指南作为医生进行科学诊疗的重要参考资料,包含大量经过临床验证的诊疗路径和建议。这些指南通常发布在权威医学网站上,数量庞大且更新频繁。如何自动化地抓取并存储这些指南,成为医疗数据分析、医学研究以及医疗 AI 模型训练等工作的关键第一步。本文将系统讲解如何利用 Python 爬虫技术从权威医学网站爬取临床指南内容,并实现 PDF 解析与结构化存储。

u014481728的博客 449

虚继承

  尽管在派生列表中同一个基类只能出现一次,但实际上派生类可以多次继承同一个类。派生类可以通过它的两个直接基类分别继承同一个间接基类,也可以直接继承某个基类,然后通过另一个基类再一次间接继承该类。   举个例子,IO标准库的istream和ostream分别继承了一个共同的名为base_ios的抽象基类。该抽象基类负责保存流的缓冲内容并管理流的条件状态。iostream是另外一个类,它...

dnhm47322的博客 176

【数据集】中国陆地生态系统通量观测网 ChinaFLUX

【数据集】中国陆地生态系统通量观测网 ChinaFLUX

WW、forever的博客 185

C++虚继承与虚基类的本质

我的新浪微博:http://weibo.com/freshairbrucewoo。 欢迎大家相互交流,共同提高技术。  声明:此篇博客网上转载,转载原因是对于虚拟继承分析得比较彻底:让我很好的理解了虚拟继承的作用于带来的坏处(性能的损失)。    虚继承和虚基类的定义是非常的简单的,同时也是非常容易判断一个继承是否是虚继承的,虽然这两个概念的定义是非常的简单明确的,但是在C...

weixin_30491641的博客 78

visio安装包

Microsoft Office Visio 是一款专业的制图软件。利用经过更新的形状、协作工具和数据链接图表,创建专业级图表以简化复杂的信息。

迈瑞BS系列全自动生化分析仪LIS协议接口手册(内含HL7和ASTM)

迈瑞BS系列全自动生化分析仪LIS协议接口手册,内含HL7和ASTM)

【C++入门】虚继承的实现原理

在这个例子中,类B中的vbptr指向了虚表D::$vbtable@B@,虚表表明公共基类A的成员变量dataA距离类B开始处的位移为20,这样就找到了成员变量dataA,而虚继承也不用像普通多继承那样维持着公共基类的两份同样的拷贝,节省了存储空间。再由B和C派生出D,此时D包含了B和C的成员。对于类B和C,sizeof的值变成了12,除了包含类A的成员变量dataA外还多了一个指针vbptr,类D除了继承B、C各自的成员变量dataB、dataA和自己的成员变量外,还有两个分别属于B、C的指针。

weixin_42031299的博客 377

Camera2Demo

在Camera2 的基础上进行封装,使得Camera2一堆初始化逻辑与Activity分离,降低耦合度,完成预览拍照并保存的操作

虚函数指针和虚基类指针

版权声明:本文为博主原创文章,遵循 CC 4.0 by-sa 版权协议,转载请附上原文出处链接和本声明。 本文链接:https://blog.youkuaiyun.com/xiaxzhou/article/details/76576516 本文参考了 http://www.cnblogs.com/jerry19880126/p/3616999.html 上述博客给出了以下结论: 每个类都有虚指针和虚表;...

qq_30397601的博客 1022

温度PID控制PLC程序

温度PID稳定控制程序,适用于欧姆龙PLC,对其他PLC也具有很大参考价值

C++多重继承分析——《虚继承实现原理(虚继承和虚函数)》

博客转载:https://blog.youkuaiyun.com/longlovefilm/article/details/80558879 一、虚继承和虚函数概念区分 虚继承和虚函数是完全无相关的两个概念。 虚继承是解决C++多重继承问题的一种手段,从不同途径继承来的同一基类,会在子类中存在多份拷贝。这将存在两个问题:其一,浪费存储空间;第二,存在二义性问题,通常可以将派生类对象的地址赋值给...

weixin_30932215的博客 230

虚基类(虚继承) 原理

原文地址:点击打开链接 在《深度探索C++对象模型》里,有一个问题,也是去公司面试的时候那些技术人员常问的问题:在C++中,obj是一个类的对象,p是指向obj的指针,该类里面有个数据成员mem,请问obj.mem和p->mem在实现和效率上有什么不同。 答案是:只有一种情况下才有重大差异,该情况必须满足以下3个条件: (1)、obj 是一个虚拟继承的派生类的对象

royliu1的专栏 774

虚继承内存布局,构造顺序

转自:http://www.cnblogs.com/ziolo/archive/2013/05/07/3066022.html 一直疑惑虚继承与虚函数并存时类的内存结构,下面的文章虽然格式乱但是值得细细品味,我在vs2010下查看了下类的内存结构,除了没有“0x00000000  // 虚基类的非虚基类的分隔符”外,内存结构基本与文中所述一样,另外补充一下虚基类的非虚基类A的偏移地址由8

433

多重继承与虚继承编程实验

多重继承与虚继承编程实验基本知识多重继承多重继承下的类作用域虚继承构造函数与虚继承关于本程序示例代码Animal_virtual_baseVers.hvirt-inherit.cpp运行结果 本文是本人大一期间在校学习C++课程时所撰写的实验报告的摘录,由于刚上大学,刚接触计算机编程方面的相关知识,故可能会有很多不足甚至错误的地方,还请各位看官指出及纠正。 本文所涉及到的“教材”指: 电子工业出版社《C++ Primary中文版(第5版)》 如需转载或引用请标明出处。 基本知识 多重继承 多重继承在概念上比

qq_43266093的博客 479

【C++11】 从内存布局看C++虚继承的实现原理

转载自:http://blog.youkuaiyun.com/xiejingfa/article/details/48028491 准备工作 1、VS2012使用命令行选项查看对象的内存布局 微软的Visual Studio提供给用户显示C++对象在内存中的布局的选项:/d1reportSingleClassLayout。使用方法很简单,直接在[工具(T)]选项下找到“Visual Studio命...

realzuile的博客 610

C++学习之路: 虚继承的内存的模型

写给出结论: 1.类大小计算遵循结构体对其原则 2.类的大小与数据成员有关,与成员函数无关 3.类的大小与静态数据成员无关 4. 虚继承对类的大小影响 5.虚函数对类的大小影响 研究背景) 我们以这个钻石型继承来研究下,虚继承后派生类DD的内存分布,先说原理和推测,来理解编译器的处理是如何在内存上体现的。 保持我们一贯的做风。 #include <...

apeng139423的博客 280

虚函数 虚基类 虚继承

  该文转载自http://blog.youkuaiyun.com/wanggang_0717/article/details/4313906 虚基 类 (虚 继 承)       1, 一个 类 可以在一个 类 族中既被用作虚基 类 ,也被用作非虚基 类 。       2, 在派生 类 的 对 象中,同名的虚基 类 只 产 生一个虚基 类 子 对 象,而某个非虚基 类产 生各自的子 对 象。  ...

qiudesuo的专栏 312

【C++拾遗】 从内存布局看C++虚继承的实现原理

2019独角兽企业重金招聘Python工程师标准>>> ...

weixin_34244102的博客 93

菱形继承解决方案--虚基类

两个派生类继承同一个基类而又有某个类同时继承者两个派生类,这种继承被称为菱形继承,或者钻石型继承。 这种继承所带来的问题: 羊继承了动物的数据和函数,鸵同样继承了动物的数据和函数,当草泥马调用函数或者数据时,就会产生二义性。 草泥马继承自动物的函数和数据继承了两份,其实我们应该清楚,这份数据我们只需要一份就可以。 classBigBase{ public: BigBase(){mParam =0;} voidfunc(){cout <<"B...

306

从内存布局看C++虚继承的实现原理

原创作品,转载请标明:http://blog.youkuaiyun.com/xiejingfa/article/details/48028491 准备工作 1、VS2012使用命令行选项查看对象的内存布局 微软的Visual Studio提供给用户显示C++对象在内存中的布局的选项:/d1reportSingleClassLayout。使用方法很简单,直接在[工具...

GUO-QI的博客 413
上一篇: 关于类对象大小的 sizeof 计算问题(续)
下一篇: [转载]GNU Emacs 常用快捷键
lambert_s
博客等级 码龄17年 31粉丝 16原创
评论 1
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值