C++ 虚拟继承

深入探究C++虚拟继承 C++的继承作为它的一个特性,必须要做到深入了解,对于C++继承方式,我们之前讲过,有单继承,多继承,菱形继承,虚拟继承,菱形虚拟继承。 这次我们来深入探究一下虚拟继承和菱形虚拟继承。不过在此之前,先让我们简单的了解一下他们的作用。 在不考虑含有虚拟继承的情况下,C++继承模型中大多数情况下是菱形继承,虽然菱形继承是一个很有用的继承方式,但是它存在一个问题,就是访问二义性的问题,而这时我们引入... 阅读详情
C++ 虚拟继承

1.为什么要引入虚拟继承

虚拟继承是多重继承中特有的概念。虚拟基类是为解决多重继承而出现的。如:类D继承自类B1、B2,而类B1、B2都继 承自类A,因此在类D中两次出现类A中的变量和函数。为了节省内存空间,可以将B1、B2对A的继承定义为虚拟继承,而A就成了虚拟基类。实现的代码如 下:

class A

class B1:public virtual A;

class B2:public virtual A;

class D:public B1,public B2;

虚拟继承在一般的应用中很少用到,所以也往往被忽视,这也主要是因为在C++中,多重继承是不推荐的,也并不常用,而一旦离开了多重继承,虚拟继承就完全失去了存在的必要因为这样只会降低效率和占用更多的空间。

为什么需要虚继承?

由于C++支持多重继承,那么在这种情况下会出现重复的基类这种情况,也就是说可能出现将一个类两次作为基类的可能性。比如像下面的情况

复制代码
 1 #include<iostream>
 2 using std::cout;
 3 using std::endl;
 4 class Base
 5 {
 6 protected:
 7 int value;
 8 public:
 9 Base()
10 {
11 cout<<"in Base"<<endl;
12 }
13 };
14 class DerivedA:protected Base
15 {
16 public:
17 DerivedA()
18 {
19 cout<<"in DerivedA"<<endl;
20 }
21 };
22 class DerivedB: protected Base
23 {
24 public:
25 DerivedB()
26 {
27 cout<<"in DerivedB"<<endl;
28 }
29 };
30 class MyClass:DerivedA,DerivedB
31 {
32 public:
33 MyClass()
34 {
35 cout<<"in MyClass"<<value<<endl;
36 }
37 };
复制代码

编译时的错误如下


这 中情况下会造成在MyClass中访问value时出现路径不明确的编译错误,要访问数据,就需要显示地加以限定。变成DerivedA::value或 者DerivedB::value,以消除歧义性。并且,通常情况下,像Base这样的公共基类不应该表示为两个分离的对象,而要解决这种问题就可以用虚 基类加以处理。如果使用虚继承,编译便正常了,类的结构示意图便如下。

虚继承的特点是,在任何派生类中的virtual基类总用同一个(共享)对象表示,正是如上图所示。

2.引入虚继承和直接继承会有什么区别呢

由于有了间接性和共享性两个特征,所以决定了虚继承体系下的对象在访问时必然会在时间和空间上与一般情况有较大不同。

2.1时间:在通过继承类对象访问虚基类对象中的成员(包括数据成员和函数成员)时,都必须通过某种间接引用来完成,这样会增加引用寻址时间(就和虚函数一样),其实就是调整this指针以指向虚基类对象,只不过这个调整是运行时间接完成的。

2.2空间:由于共享所以不必要在对象内存中保存多份虚基类子对象的拷贝,这样较之 多继承节省空间。虚拟继承与普通继承不同的是,虚拟继承可以防止出现diamond继承时,一个派生类中同时出现了两个基类的子对象。也就是说,为了保证 这一点,在虚拟继承情况下,基类子对象的布局是不同于普通继承的。因此,它需要多出一个指向基类子对象的指针。

3.笔试,面试中常考的C++虚拟继承的知识点

第一种情况:         第二种情况:          第三种情况            第四种情况:
class a           class a              class a              class a
{              {                {                 {
    virtual void func();      virtual void func();       virtual void func();        virtual void func();
};              };                  char x;              char x;
class b:public virtual a   class b :public a           };                };
{              {                class b:public virtual a      class b:public a
    virtual void foo();        virtual void foo();     {                 {
};              };                  virtual void foo();        virtual void foo();
                               };                };

如果对这四种情况分别求sizeof(a),  sizeof(b)。结果是什么样的呢?下面是输出结果:(在vc6.0中运行)
第一种:4,12
第二种:4,4
第三种:8,16
第四种:8,8

详细分析可参考:http://blog.youkuaiyun.com/wangqiulin123456/article/details/8059536

想想这是为什么呢?

因为每个存在虚函数的类都要有一个4字节的指针指向自己的虚函数表,所以每种情况的类a所占的字节数应该是没有什么问题 的,那么类b的字节数怎么算呢?看“第一种”和“第三种”情况采用的是虚继承,那么这时候就要有这样的一个指针vptr_b_a,这个指针叫虚类指针,也 是四个字节;还要包括类a的字节数,所以类b的字节数就求出来了。而“第二种”和“第四种”情况则不包括vptr_b_a这个指针,这回应该木有问题了 吧。

复制代码
复制代码
1 class a
2 {
3     virtual void func();
4 };
5 
6 class b:public a
7 {
8     void foo();
9 };
复制代码
复制代码

此时:sizeof(a) = 4 , sizeof(b) = 4

复制代码
1 class a
2 {
3     virtual void func();
4 };
5 
6 class b:public  a
7 {
8  virtual void foo();
9 };
复制代码

奇怪的是,此时:sizeof(a) = 4 , sizeof(b) = 4。 尽管class b中在voif foo()前加了virtual,但结果却相同。

复制代码
1 class a
2 {
3     virtual void func();
4 };
5 
6 class b:public virtual a
7 {
8     void foo();
9 };
复制代码

此时:sizeof(a) = 4 , sizeof(b) = 8

复制代码
复制代码
1 class a
2 {
3    void func();
4 };
5 
6 class b:public a
7 {
8     virtual void foo();
9 };
复制代码
复制代码

此时:sizeof(a) = 1 , sizeof(b) = 4

复制代码
复制代码
1 class a
2 {
3    void func();
4 };
5 
6 class b:public a
7 {
8     void foo();
9 };
复制代码
复制代码

此时:sizeof(a) = 1 , sizeof(b) = 1

如下例:

复制代码
复制代码
复制代码
 1 class A
 2 {
 3 };
 4 class A2
 5 {
 6 };
 7 class B : public A
 8 {
 9 };
10 class C : public virtual B
11 {
12 };
13 class D : public A , public A2
14 {
15 };
复制代码
复制代码
复制代码

以上答案分别是1 , 1 , 4 , 1. 这说明:空类所占空间为1,单一继承的空类空间也为1,多重继承的空类空间还是1.但是虚继承涉及到虚表(虚指针),所以sizeof(C)的大小为4

我相信经过上面的分析和对比,以后看到这类问题不会再疑惑,会有一种“水落石出”的感觉。

关于字节的求取更详细的总结见:http://www.cnblogs.com/heyonggang/p/3253036.html

                                         http://www.cnblogs.com/heyonggang/archive/2012/12/11/2812304.html

4.c++重载、覆盖、隐藏的区别和执行方式

既然说到了继承的问题,那么不妨讨论一下经常提到的重载,覆盖和隐藏
4.1成员函数被重载的特征
(1)相同的范围(在同一个类中);
(2)函数名字相同;
(3)参数不同;
(4)virtual 关键字可有可无。
4.2“覆盖”是指派生类函数覆盖基类函数,特征是:
(1)不同的范围(分别位于派生类与基类);
(2)函数名字相同;
(3)参数相同;
(4)基类函数必须有virtual 关键字。
4.3“隐藏”是指派生类的函数屏蔽了与其同名的基类函数,特征是:

(1)如果派生类的函数与基类的函数同名,但是参数不同,此时,不论有无virtual关键字,基类的函数将被隐藏(注意别与重载混淆)。
(2)如果派生类的函数与基类的函数同名,但是参数相同,但是基类函数没有virtual 关键字。此时,基类的函数被隐藏(注意别与覆盖混淆)。

小结:说白了就是如果派生类和基类的函数名和参数都相同,属于覆盖,这是可以理解的吧,完全一样当然要覆盖了;如果只是函数名相同,参数并不相同,则属于隐藏。


4.4 三种情况怎么执行:

4.4.1 重载:看参数。

4.4.2 隐藏:用什么就调用什么。

4.4.3 覆盖:调用派生类。

5.C++子类继承父类后子类的大小

复制代码
复制代码
 1 #include <iostream>
 2 using namespace std;
 3 class  A 
 4 {
 5 private:
 6  int a;
 7 };
 8 
 9 class B:public  A
10 {
11 private:
12  int b;
13 };
14 
15 int main()
16 {
17  cout<<sizeof(A)<<endl;
18  cout<<sizeof(B)<<endl;
19  return 0;
20 }
复制代码
复制代码

刚开始我一想子类继承父类不会继承父类的私有变量,如此我认为结果为4,4(错误)。而事实上结果是4,8。也就是说子类把父类的私有变量也继承下来了,但是却无法访问,对于我这种菜鸟来说一下子没法转个弯来,后来看看资料焕然大悟,子类虽然无法直接访问父类的私有变量,但是子类继承的父类的函数却可以访问,不然的话如果只继承函数而不继承变量,哪么父类的函数岂不成了无米之炊了。所以必须把父类的所有变量都继承下来,这样既能保护父类的变量也能使用父类的函数。

C++的虚拟继承机制 由于我对这方面不是很明白,所以只好实验来让我更好的理解,将结果记录在这里吧~   理论   我们采用第一个定义,也是说被virtual所修饰的事物或现象在本质上是存在的,但是没有直观的形式表现,无法直接描述或定义,需要通过其他的间接方式或手段才能够体现出其实际上的效果。   那么在C++中是采用了这个词意,不可以在语言模型中直接调用或体现的,但是确实是存在可以被间接的方式进行调用或体现的。比如:虚函数必须要通过一种间接的运行时(而不是编译时)机制才能够激活(调用)的函数,而虚继承也是必须在运行时才能够进行定位访问的一种体制。存在,但间接。其中关键在于存在、间接和共享这三种特征。 立即下载

相关推荐

告别糊涂账!用CKM3底表逻辑自建物料成本监控报表(含ABAP代码片段)

本文详细介绍了如何利用CKM3底表逻辑自建物料成本监控报表,解决标准报表无法穿透成本差异根源的问题。通过ABAP代码片段和四步架构法,帮助财务团队精准分析成本构成,优化成本核算批量处理,实现多维透视差异分析,提升成本监控效率。

weixin_30740295的博客 267

C++语法——详解虚继承

一.什么是虚继承所谓虚继承(virtual)就是子类中只有一份间接父类的数据。该技术用于解决多继承中的父类为非虚基类时出现的数据冗余问题,即菱形继承问题。,那么,当我们用D实例化对象调用a时,编译会报错,因为发生了混淆。除非指定类域B或C。当然指定A也可以,因为默认会从第一个父类中找。此时,D的实例化对象内部结构如下:而当我们使用虚继承时,结构是下图这样,D中只有一份父类A,当我们调用A中数据时,并不会发生冗余。

weixin_61857742的博客 3万+

ANSYS 有限元分析 接触分析

目 录一、前沿二、接触单元类三、接触单元关键选项3.1 CONTA174 KeyOption3.2 TARGE170 KeyOption四、接触分析标准步骤4.1 定义实常数4.2 定义接触和目标单元4.3 创建接触和目标单元五、接触分析实例5.1 MPC5.2 标准接触六、尾声七、参考文献 阿阳的 ANSYS 使用指南,本文仅用于个人学习,除此之外,无其他任何用途。 ANSYS 有限元分析 概述 ANSYS 有限元分析 坐标系/工作平面 ANSYS 有限元分析 几何建模 ANSY

Hulunbuir 4万+

重学C++系列之虚继承

如何解决C++中的多继承中基类成员重名,产生的二义性,虚继承你值得拥有。

AABond的博客 5071

Python读取Matlab v7.3数据

文章目录Matlab保存数据Python读取 v7.3 Mat Matlab保存数据 在matlab设置中,常规->MAT文件,选择MATLAB版本7.3或更高版本保存数据。 注意保存数据的文件名为 corre.mat,保存的数据数组为data; 后面python的读取中会用到这两个参数 Python读取 v7.3 Mat 先安装hdf5storage库,再使用以下代码,就可以读取到mat中的数据,注意 import hdf5storage import numpy as np dataDi

weixin_43121914的博客 1081

Clion编译stm32cube初始化工程时non constant or forward reference address expression for section .ARM.extab报错

相信应该有同学在看了稚晖君关于clion开发stm32的文章之后也想在自己PC上实现“优雅の嵌入式开发”遇到和我类似的问题。他的文章是22年写的,我个人猜测应当是版本更新导致的不适配。意思就是说 READONLY 只能在gcc11以后的版本中使用,gcc10及以前的版本将无法正确识别,也就有如下错误。或者就可以将gcc升级至最新的版本。记得在clion工具链中修改c,cpp编译器的位置。可以将文本括号中的 READONLY 全部删除。当时由于没有注意到这行注释导致在这里卡了好久。

2301_77256849的博客 2512

C++ 虚拟继承问题

文章目录前言为什么要有虚拟继承虚拟继承和普通继承的区别虚拟继承 sizeof() 问题 前言 本篇文章主要描述虚拟继承中内存分布情况,当然,可能会不太完全正确,希望大家多多指正 为什么要有虚拟继承 我们看下面一种情景: class A { public: int a; }; class B : public A { public: int b; }; class C : public A ...

The Laughing Uncle的博客 9963

【企业智能体开发】优化模型成本、延迟与失败降级

小林的培训快开始时,服务台花很久“思考最优回答”,体验并不会更好。反过来,为了省钱而始终选最轻的模型,若连房间、连接方式和任务状态都识别不稳,后续错误检索和重复建单会造成更大代价。企业 Agent 的优化对象不是单次模型调用价格,而是任务在可接受时间内、安全地推进到正确结果的总成本。

Mr数据杨 893

C++.继承——虚继承

C++.继承——虚继承

xiaoyu1381的博客 2394

AD9361功能介绍和使用

AD9361使用分数分频锁相环(PLL)来产生发射机和接收机本地振荡器(LO)频率和BB PLL。BB PLL 可用于数据转换、数字滤波器和 |/O 端口。PLL的参考时钟可以由外部振荡器或外部晶体(XO)和数字可编程片内可变电容提供,电容微调产生参考时钟频率,XO和微调电容的组合统称为DCXO。有多个天线输入的分集系统。接收链数据首先经过低噪声放大器(LNA),然后是同相(1)和正交(Q)放大器,混频器和频带整形滤波器,可将接收到的信号下变频为基带以进行数字化。时分双工(TDD)和频分双工(FDD)。。。

wangjie36的博客 1万+

【AI智能体】AI 编程 + 飞书CLI + Skills 重塑开发新范式实战操作详解

AI 编程 + 飞书CLI + Skills 重塑开发新范式实战操作详解

congge 3666

C++虚拟继承

CChildA虚拟继承CParent,编译器会在它的对象的首地址的位置,添加一个虚基类表指针vbptr指向派生类(CChildA)的virtual bass class table(虚基类表),虚基类表中存放的是派生类的虚基类表指针到虚基类实例指针的偏移量。CChildC的大小应该是:2个vbptr + 1个继承自CChildA的m_nChildA + 1个继承自CChildB的m_nChildB + 1份自己新增的m_nChildC + 1份CParent m_nParent拷贝。

bkspiderx的博客 1095

【Android逆向工程】第18章:Ollvm 混淆与对抗

本章介绍了Ollvm混淆技术及其对抗方法。Ollvm是基于LLVM的开源代码混淆工具,主要实现控制流平坦化、虚假控制流和指令替换三种混淆技术。控制流平坦化通过状态变量和switch-case结构隐藏真实控制流,将原本清晰的逻辑拆分为多个状态块。混淆后的代码在静态分析中难以理解,但可通过动态追踪执行路径、符号执行等方法来还原原始逻辑。本章还提供了混淆代码示例和对抗策略,帮助理解Ollvm的工作原理及其防御方法。

w987333120的博客 213

Cpp || 菱形虚拟继承

菱形的继承方式带来的冗余问题

Template Code 507

Probability and Statistics - 4th edition

Probability and Statistics - 4th edition

visio安装包

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

关于C++中的虚拟继承的一些总结

1.为什么要引入虚拟继承 虚拟继承是多重继承中特有的概念。虚拟基类是为解决多重继承而出现的。如:类D继承自类B1、B2,而类B1、B2都继承自类A,因此在类D中两次出现类A中的变量和函数。为了节省内存空间,可以将B1、B2对A的继承定义为虚拟继承,而A就成了虚拟基类。实现的代码如下: class A class B1:public virtual A; class B2:public vi

skiing_886的专栏 8164

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

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

【C++】继承深度解析:继承方式和菱形虚拟继承的详解

本文系统讲解了 C++ 中的继承机制,包括单继承、多继承、菱形继承及虚拟继承,配合图解和代码示例直观展示各类继承的对象模型、内存布局及访问特点,重点说明虚拟继承如何通过共享基类实例、虚基表偏移和构造顺序,解决菱形继承的数据冗余与二义性问题。

qq_2778465975的博客 2843

RU DOS版、EFI版32bit/64bit BIOS常用工具

DOS版、EFI版的RU工具,可用来查看IO、MEM、PCI、SMBIOS等资源。亲测在EFI SHELL下可用。

彻底弄懂C++虚拟继承

为了解决普通继承的“数据冗余”和“二义性”的问题,C++引入了虚拟继承使得在多重继承下,基类成员只继承一份。 这篇博客主要解析1.虚拟继承机制,如何做到的? 2.它和普通继承有什么区别?3.虚拟继承同时满足多态会怎样? .........

weixin_44212838的博客 1万+

【C++】继承(定义、菱形继承、虚拟继承)

💬 hello!各位铁子们大家好哇。今日更新了继承的相关内容Person是父类,也称作基类。Student是子类,也称作派生类。一般不建议设计出多继承,一定不要设 计出菱形继承。否则在复杂度及性能上都有问题。

qinjh_的博客 3152

C++ 多重继承 虚拟继承的使用

转载自:http://www.cppblog.com/zmllegtui/archive/2008/10/28/65385.aspx 面向对象编程语言中的多重继承指的是一个类别可以同时从多于一个父类继承行为与特征的功能。与单一继承相对,单一继承指一个类别只可以继承自一个父类。     重温Java,发现Java竟然不支持类多重继承(直接继承类),却允许接口的多重继承。。     C+

miyunhong的专栏 2132

C++ 中 虚拟继承 的概念

C++中虚拟继承的概念 为了解决从不同途径继承来的同名的数据成员在内存中有不同的拷贝造成数据不一致问题,将共同基类设置为虚基类。这时从不同的路径继承过来的同名数据成员在内存中就只有一个拷贝,同一个函数名也只有一个映射。这样不仅就解决了二义性问题,也节省了内存,避免了数据不一致的问题。 class 派生类名:virtual 继承方式 基类名 virtual是关键字,声明该基类为派生类的虚基类。

李根生 的专栏 2033

C++的类中的虚拟继承

C++中的虚拟继承(Virtual Inheritance)是一种特殊的继承方式,主要用于解决多重继承时可能出现的"菱形继承问题"(Diamond Problem)。这种继承机制通过virtual关键字实现,能让派生类只保留一份基类的实例,从而避免数据冗余和访问歧义。菱形继承问题的典型场景是:假设有一个基类A,两个派生类B和C都继承自A,然后类D又同时继承B和C。这样D中就会有两份A的成员,导致访问冲突。

Made In SQL 1013
上一篇: C++重要知识点小结---3
下一篇: C++重要知识点小结---2
大老鼠
博客等级 码龄15年 14粉丝 5原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值