C++ 虚基类、虚继承

C++ 虚继承(虚基类表指针与虚基类表) 在C++中,我们会遇到virtual这个关键字,但是它有两种含义:虚函数和虚继承,它们两个是完全无相关的两个概念。 什么是虚继承         虚继承是解决C++多重继承问题的一种手段,从不同途径继承来的同一基类,会在子类中存在多份拷贝。这将存在两个问题:其一,浪费存储空间;第二,存在二义性问题,通常可以将派生类对象的地址赋值给基类对象,实现的具体方式是,将基类指针指向继承类(继承类有基类的... 阅读详情

C++中虚拟继承的概念

为了解决从不同途径继承来的同名的数据成员在内存中有不同的拷贝造成数据不一致问题,将共同基类设置为虚基类。这时从不同的路径继承过来的同名数据成员在内存中就只有一个拷贝,同一个函数名也只有一个映射。这样不仅就解决了二义性问题,也节省了内存,避免了数据不一致的问题。
class 派生类名:virtual 继承方式  基类名
virtual是关键字,声明该基类为派生类的虚基类。
在多继承情况下,虚基类关键字的作用范围和继承方式关键字相同,只对紧跟其后的基类起作用。
声明了虚基类之后,虚基类在进一步派生过程中始终和派生类一起,维护同一个基类子对象的拷贝。

 

C++虚拟继承

◇概念:

C++使用虚拟继承(Virtual Inheritance),解决从不同途径继承来的同名的数据成员在内存中有不同的拷贝造成数据不一致问题,将共同基类设置为虚基类。这时从不同的路径继承过来的同名数据成员在内存中就只有一个拷贝,同一个函数名也只有一个映射。

 

◇解决问题:

解决了二义性问题,也节省了内存,避免了数据不一致的问题。
 
◇同义词: 
虚基类(把一个动词当成一个名词而已)
当在多条继承路径上有一个公共的基类,在这些路径中的某几条汇合处,这个公共的基类就会产生多个实例(或多个副本),若只想保存这个基类的一个实例,可以将这个公共基类说明为虚基类。

 

◇语法:

class 派生类: virtual 基类1,virtual 基类2,...,virtual 基类n

{

...//派生类成员声明

};

 

◇执行顺序

首先执行虚基类的构造函数,多个虚基类的构造函数按照被继承的顺序构造;

执行基类的构造函数,多个基类的构造函数按照被继承的顺序构造;

执行成员对象的构造函数,多个成员对象的构造函数按照申明的顺序构造;

执行派生类自己的构造函数;

析构以与构造相反的顺序执行;

mark

从虚基类直接或间接派生的派生类中的构造函数的成员初始化列表中都要列出对虚基类构造函数的调用。但只有用于建立对象的最派生类的构造函数调用虚基类的构造函数,而该派生类的所有基类中列出的对虚基类的构造函数的调用在执行中被忽略,从而保证对虚基类子对象只初始化一次。

在一个成员初始化列表中同时出现对虚基类和非虚基类构造函数的调用时,虚基类的构造函数先于非虚基类的构造函数执行。

 

◇因果:

多重继承->二义性->虚拟继承解决

 

◇二义性:

  1: //-----------------------------------------------------

 2: //名称:blog_virtual_inherit.cpp 
 3: //说明:C++虚拟继承学习演示 
 4: //环境:VS2005 
 5: //blog:pppboy.blog.163.com 
 6: //---------------------------------------------------- 
 7: #include "stdafx.h"
 8: #include <iostream>
 9: using namespace std;
 10:
 11: //Base 
 12: class Base
 13: {
 14: public:
 15: Base(){cout << "Base called..."<< endl;}
 16: void print(){cout << "Base print..." <<endl;}
 17: private:
 18: };
 19:
 20: //Sub 
 21: class Sub //定义一个类 Sub 
 22: {
 23: public:
 24: Sub(){cout << "Sub called..." << endl;}
 25: void print(){cout << "Sub print..." << endl;}
 26: private:
 27: };
 28:
 29: //Child 
 30: class Child : public Base , public Sub //定义一个类Child 分别继承自 Base ,Sub 
 31: {
 32: public:
 33: Child(){cout << "Child called..." << endl;}
 34: private:
 35: };
 36:
 37: int main(int argc, char* argv[])
 38: {
 39: Child c;
 40:
 41: //不能这样使用,会产生二意性,VC下error C2385 
 42: //c.print();  
 43:
 44: //只能这样使用 
 45: c.Base::print();
 46: c.Sub::print();
 47:
 48: system("pause");
 49: return 0;
 50: }

 

◇多重继承:

 1: //-----------------------------------------------------  
 2: //名称:blog_virtual_inherit.cpp  
 
3: //说明:C++虚拟继承学习演示  
 4: //环境:VS2005  
 5: //blog:pppboy.blog.163.com  
 6: //----------------------------------------------------  
 7: #include "stdafx.h"  
 8: #include <iostream>
 9: using namespace std;  
 10:
 11: int gFlag = 0;  
 12:
 13: class Base  
 14: {
 15: public:  
 16: Base(){cout << "Base called : " << gFlag++ << endl;}  
 17: void print(){cout << "Base print" <<endl;}  
 18: };
 19:
 20: class Mid1 : public Base  
 21: {
 22: public:  
 23: Mid1(){cout << "Mid1 called" << endl;}  
 24: private:  
 25: };
 26:
 27: class Mid2 : public Base  
 28: {
 29: public:  
 30: Mid2(){cout << "Mid2 called" << endl;}  
 31: };
 32:
 33: class Child:public Mid1, public Mid2  
 34: {
 35: public:  
 36: Child(){cout << "Child called" << endl;}  
 37: };
 38:
 39: int main(int argc, char* argv[])  
 40: {
 41: Child d;
 42:
 43: //不能这样使用,会产生二意性  
     //d.print();  
 45:
 46: //只能这样使用  
 47: d.Mid1::print();
 48: d.Mid2::print();
 49:
 50: system("pause");  
 51: return 0;  
 52: }
 53:

//output

 Base called : 0
 Mid1 called
 Base called : 1 
 Mid2 called
 Child called
 Base print
 Base print

 

◇虚拟继承

在派生类继承基类时,加上一个virtual关键词则为虚拟继承

 1: //-----------------------------------------------------  
 2: //名称:blog_virtual_inherit.cpp  
 
3: //说明:C++虚拟继承学习演示  
 4: //环境:VS2005  
 5: //blog:pppboy.blog.163.com  
 6: //----------------------------------------------------  
 7: #include "stdafx.h"  
 8: #include <iostream>
 9: using namespace std;  
 10:
 11: int gFlag = 0;  
 12:
 13: class Base  
 14: {
 15: public:  
 16: Base(){cout << "Base called : " << gFlag++ << endl;}  
 17: void print(){cout << "Base print" <<endl;}  
 18: };
 19:
 20: class Mid1 : virtual public Base  
 21: {
 22: public:  
 23: Mid1(){cout << "Mid1 called" << endl;}  
 24: private:  
 25: };
 26:
 27: class Mid2 : virtual public Base  
 28: {
 29: public:  
 30: Mid2(){cout << "Mid2 called" << endl;}  
 31: };
 32:
 33: class Child:public Mid1, public Mid2  
 34: {
 35: public:  
 36: Child(){cout << "Child called" << endl;}  
 37: };
 38:
 39: int main(int argc, char* argv[])  
 40: {
 41: Child d;
 42:
 43: //这里可以这样使用  
 44: d.print();
 45:
 46: //也可以这样使用  
 47: d.Mid1::print();
 48: d.Mid2::print();
 49:
 50: system("pause");  
 51: return 0;  
 52: }
 53:

//output

 1: Base called : 0
 2: Mid1 called
 3: Mid2 called
 4: Child called
 5: Base print
 6: Base print
 7: Base print
 8: 请按任意键继续. . .

 

◇通过输出的比较

1.在多继承情况下,虚基类关键字的作用范围和继承方式关键字相同,只对紧跟其后的基类起作用。
2.声明了虚基类之后,虚基类在进一步派生过程中始终和派生类一起,维护同一个基类子对象的拷贝。
3.观察类构造函数的构造顺序,拷贝也只有一份。
 
◇与虚函数关系 
虚拟继承与虚函数有一定相似的地方,但他们之间是绝对没有任何联系的。
再想一次:虚拟继承,虚基类,虚函数。
 
◇菱形结构虚继承对象的内存模型  
 
转载:
http://blog.youkuaiyun.com/wangxingbao4227/article/details/6772579
 
 
一道实际的面试题:

代码如下(代码来自于何海涛《程序员面试精选100题第32题)。意图是要设计一个不能被继承的类,类似java中的final。但是又可以生成栈对象,可以像一般的C++类一样使用。

#include<iostream>

using std::endl;

using std::cout;

template <class T> class MakeFinal

{

friend T;

private:

MakeFinal()

{

cout<<"in MakeFinal"<<endl;

}

~MakeFinal(){}

};

 

class FinalClass: virtual public MakeFinal<FinalClass>

{

public:

FinalClass()

{

cout<<"in FinalClass"<<endl;

}

~FinalClass(){}

};

class Try: public FinalClass

{

public:

Try()

{

cout<<"in Try"<<endl;

}

};

这样的确使得FinalClass不能被继承了,原因在于类FinalClass是从类MakeFinal<Final>虚继承过来的,在调用Try的构造函数的时候,会直接跳过FinalClass而直接调用MakeFinal<FinalClass>的构造函数。而Try不是MakeFinal<Final>的友元,所以这里就会出现编译错误。但是如果把虚继承改成一般的继承,这里就没什么问题了。

C++ 内存布局 - Part6: 虚继承 虚继承可以在菱形继承体系中,防止派生类中有多份重复祖基类内容。如下图所示,如果是常规继承,Class Final中会有两份Class Base的内容。通过虚继承,即Derived1 虚继承自Base, Derived2 也虚继承自Base, 那么Final中将最终保留一份Base部分的内容。 阅读详情

相关推荐

C++基础知识 - 虚基类和虚继承

虚基类和虚继承 多继承时很容易产生命名冲突,即使我们很小心地将所有类中的成员变量和成员函数都命名为不同的名字,命名冲突依然有可能发生,比如典型的是菱形继承,如下图所示: 菱形继承 类 A 派生出类 B 和类 C,类 D 继承自类 B 和类 C,这个时候类 A 中的成员变量和成员函数继承到类 D 中变成了两份,一份来自 A–>B–>D 这条路径,另一份来自 A–>C–>D 这条路径。 在一个派生类中保留间接基类的多份同名成员,虽然可以在不同的成员变量中分别存放不同的数据,但大多数情况

骆驼胡杨的博客 514

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

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

weixin_61857742的博客 3万+

C++的虚拟继承机制

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

重学C++系列之虚继承

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

AABond的博客 5072

足球大数据分析入门:从赔率到预测的完整流程(附数据集)

本文提供了一份足球大数据分析的完整实战指南,涵盖从数据采集、清洗、特征工程到模型构建与评估的全流程。文章详细介绍了如何利用公开数据集与API获取数据,并通过逻辑回归、随机森林等模型进行比赛结果预测,最后强调了模型评估与策略回测的重要性,帮助读者构建自己的分析框架。

r7s8t的博客 404

C++虚继承中的虚基类表

虚继承主要解决多重继承会在子类中存在多份拷贝的问题,这不仅浪费空间,而且存在二义性。 在之前的 C++ 继承中已经说过虚继承基本概念,这里不再赘述。这篇文章主要探究虚继承的原理。文章中多处给出了类实例对象的内存布局,查看其内存布局时,使用 VS 工具 /d1 reportAllClassLayout 进行查看,关于这个工具的详细介绍,请点击这里。 虚继承的实现原理 虚继承的底层实现一般与编译器相关,一般会通过虚基类表指针和虚基类表实现,先看如下这个程序: #pragma pack(1) class A {

code_peak 2577

基于Python的专利数据爬虫开发指南:从入门到实战

本文将详细介绍如何使用Python开发一个高效的专利数据爬虫系统,涵盖最新技术栈如Playwright、异步IO、反反爬策略等。文章包含完整代码实现、性能优化技巧及法律合规建议,帮助读者构建可投入生产的专利数据采集解决方案。专利数据是技术创新的重要指标,包含技术细节、申请人信息、法律状态等宝贵信息。企业竞争情报分析技术趋势预测研发方向决策知识产权战略制定。

2201_76125261的博客 1588

C++虚继承与虚基类

C++的三大特性:封装,继承,多态。继承的目的是就是为了代码重用,避免的重复代码的编写。继承分为单继承和多继承,单继承就是每次继承的基类只能有一个,属于一对一的关系;多继承则是子类可以同时继承自多个基类,拥有多个基类的特性,属于一对多的关系! 虚继承的定义 在多继承中派生类不能多次直接继承同一个基类,但是派生类的直接基类可能派生自同一个基类。例如:定义一个雇员类Employee作为顶层基类;另

云鹤起舞的博客 1815

神经进化实践:用遗传算法替代反向传播优化神经网络

神经进化用来定义应用于深度学习的特定优化模式。在本节中,我们通过遗传算法优化简单深度学习网络的权重/参数,替换在误差反向传播训练过程中的所用优化器。

盼小辉丶的博客 3620

4D毫米波雷达天线设计

多种天线布局方案介绍、天线设计要点介绍

5734

C++中的虚继承与虚基类

1.Cpp中的虚继承与虚基类 在多继承时,很容易产生命名冲突的问题,即使我们很小心地将所有类中的成员变量和成员函数都命名为不同的名字,命名冲突依然有可能发生,比如典型的是菱形继承,如下图所示: 类A派生出类B和类C,类D继承自类B和类C,这个时候类A中的成员变量和成员函数继承到类D中变成了两份,一份来自A–>B–>D这条路径,另一份来自A–>C–>D这条路径。在一个派生...

CurryCoder的个人博客 721

沉降预测算法-指数曲线法

沉降预测的指数曲线法原理和代码实现

yh523的博客 1009

C++的虚基类和虚继承

多重继承 : 好处是代码的复用 相当于一个派生类有多个基类既从A继承又从B继承,C是从A,B多继承而来,C有2个基类:A和B,C可以把A和B的成员都继承而来,复用起来被虚继承的类称作虚基类 vbptr和vbtable A被B虚继承,所以A是虚基类 我们打开命令提示符来看看A被虚继承,A本身内存布局没有变化我们看看B的内存布局: 原本应该最上面放ma,然后放mb,但是现在多了一个vbptr,那么A被B虚继承的ma跑哪里去了?跑到派生类最后面来了。vbptr指向的是 vbtable

ljy的博客 1231

302.QoS实现工具之MQC

QoS 技术可以对网络中报文进行分类处理,根据优先级提供不同的差分服务,如何实现这种差分服务呢?我们有一种强大的配置方法 - 模块化 QoS 命令行 MQC(Modular QoS Command-Line)。下面就来介绍一下 MQC。

迷逝 1393

YOLO系列专栏(六):YOLO26热力图可视化 | 解析模型推理逻辑,定位检测关键区域

YOLO26作为新一代目标检测模型,其核心优势是高效的特征提取和精准的目标定位,但模型内部的推理过程的“黑盒”特性,给我们的优化带来了很大困扰:❌ 检测精度低:不知道是模型没学到关键特征,还是特征提取不充分;❌ 出现误检/漏检:无法定位问题根源——是锚框不合适?还是特征图未捕捉到目标?❌ 模型优化盲目:调参、修改网络结构全靠“猜”,缺乏针对性;❌ 难以解释模型行为:实际项目中,需要说明模型“为什么检测到这个目标”,尤其是医疗、工业等对可解释性要求高的场景。而热力图可视化。

qq_36130719的博客 732

C++虚继承和虚基类

多继承(Multiple Inheritance)是指从多个直接基类中产生派生类的能力,多继承的派生类继承了所有父类的成员。尽管概念上非常简单,但是多个基类的相互交织可能会带来错综复杂的设计问题,命名冲突就是不可回避的一个。 多继承时很容易产生命名冲突,即使我们很小心地将所有类中的成员变量和成员函数都命名为不同的名字,命名冲突依然有可能发生,比如典型的是菱形继承,如下图所示: 图1:菱形继承 类 A 派生出类 B 和类 C,类 D 继承自类 B 和类 C,这个时候类 A 中的成员变量和成员函.

居老师的博客 347

假人有限元模型

假人模型,车辆碰撞假人有限元模型,适合做乘员安全的研究者利用

【C++】继承和派生、虚继承和虚基类、虚基类表和虚基类指针

继承和派生、虚继承和虚基类、虚基类表和虚基类指针继承和派生继承概述继承基本概念派生类中的成员继承的内容派生类定义派生类访问控制对象构造和析构对象构造和析构的调用顺序继承中的构造和析构的调用规则调用子类构造前会先调用父类构造继承中同名成员的处理方法继承中的静态成员特性非自动继承的函数多重继承虚继承和虚基类菱形继承虚继承和虚基类虚继承(Virtual Inheritance)虚基类(Virtual Base Class)继承后虚基类成员的可见性虚基类表和虚基类指针 继承和派生 继承概述 继承基本概念 c++最重

这道题我不会做...不会做啊..太难了!! 4094

pscad IEEE标准系统搭建说明

pscad IEEE标准系统搭建说明,包含典型的IEEE 9节点、14节点、30节点、39节点和118节点系统。内含标准测试系统的原始数据和潮流结果。

IMSL CNL 7.0.0 (x86-64)Crack

IMSL CNL 7.0.0 (x86-64)Crack intel的算法库破解文件,这是C++版本的破解文件

C++虚继承和虚基类详解

C++虚继承和虚基类详解

Elanie1024的博客 425

C++ 虚继承、虚基类指针、虚基类表

当一个类有虚基类时,编译器需要额外的指针来解析虚基类的偏移,从而支持动态多态。的大小可能导致对齐问题(例如,它可能是 4 字节,而后续成员需要 8 字节对齐),编译器插入。计算所需的存储空间,并决定对象的内存分布,即便还没有实际分配对象。,表明该类可能包含指针(通常为 8 字节,在 64 位系统上)和一些数据成员。生成,可用于分析 C++ 类的内存布局和虚拟继承机制的实现。这幅图片展示了 C++ 代码的编译结果,分析的是一个。的情况下,帮助对象正确解析虚基类的存储位置。类的内存布局,并报告。

xinxin的博客 1209

C++之虚继承和虚基类

C++之虚继承和虚基类虚继承 虚继承 C++中使用多重继承存在一些缺点,比如继承那章提到的命名冲突,就是典型的使用多重继承带来的混乱,下列程序也是,若A中存在成员变量a,A的派生类B和C都继承了A中protected修饰的成员变量a,那么派生类D多继承C和B时,相当于继承了两次A中的成员变量a,只不过是不同基类中的成员变量a。这个时候若没有指明调用哪个基类中的成员变量a,那么就会报错,这种情况使用虚继承就可以解决,我们先来看一下多继承中错误的写法和正确的写法。 #include <iostream&g

YANJINING 479

浅谈C++虚继承及虚基类表

上篇提到了虚函数表,在对其有一定了解之后继续深入理解C++中对象的内存布局

tinyway的专栏 2566

C++ 中的虚:虚基类和虚继承

C++ 中的虚:虚基类和虚继承 一、问题由来 以沙发床为例:沙发床继承了 沙发 和 床。 #include <iostream> #include<string> using namespace std; class Sofa { public: Sofa(float pe = .0, string cr = "black") :price_(pe), color_...

Bryan_QAQ的博客 629

22、C++中的虚继承以及虚基类

C++中虚继承 一、引入虚基类的原因: 如果有一个类有多个直接基类,而这些直接基类又有一个共同的基 类,则最低层的派生类会保留这个间接的共同基类数据成员的多份 同名成员,在访问这些同名的成员时,必须在派生类对象名后增加 直接基类名,使其惟一标识一个成员,以免产生二义性。 为了摆脱这种加直接基类名,引入了虚基类。 作用:虚继承基类,无论有多少个派生类虚拟继承这个基类,都只是给了每一个

xuxianmei的专栏 855
上一篇: 虚析构函数、纯虚析构函数
下一篇: 快速简单理解i2c标准协议
panfengsoftware
博客等级 码龄20年 38粉丝 34原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值