C++语法|多重继承详解(一)|理解虚基类和虚继承

系列汇总讲解,请移步:
C++语法|虚函数与多态详细讲解系列(包含多重继承内容)

虚基类是多重继承知识上的铺垫。

首先我们需要明确抽象类和虚基类的区别:
抽象类:有纯虚函数的类

虚基类是什么呢?请看文章!

文章目录

  • 虚基类的定义
  • 虚继承导致内存布局的改变?
  • Windows下虚基类导致的问题:内存泄漏

虚基类的定义

我们定义一个类A和类B:

class A {
public:
private:
    int ma;
};
class B : public A {
public:
private:
    int mb;
};

我们可以看出,其中实例化A的对象a占4个字节;实例化B的对象b占8个字节。我们现在搞一个虚继承:

class B : virtual public A {
public:
private:
    int mb;
};

在这里,我们的类A被virtual修饰,所以A被称为虚基类。

A 和 B 的继承关系就是虚继承

此时我们的 A 还是占 4个字节;但是 B 却占了12个字节??

虚继承导致内存布局的改变?

在不定义虚继承的时候,我们如果初始化类B,应该是如下的内存分布:

A::ma
mb

这里一共是8个字节。

如果采用虚继承,内存分布如图:

虚基类的数据一定要搬到我们派生类内存的最后面,然后在0地址(相对地址)有一个 vbptr。该指针指向的是一个虚基类表 vbtable!

虚基类表是干什么的呢?由于我们把 A::ma的数据搬到派生类内存的最后了,所以当有人来找 A::ma这个数据的时候还是会在第一个地址找,那么 vbptr 就应该告诉这个人 A::ma数据在内存中的位置,所以很显然,我们的 vbtable的第二行放的就是 A::ma 数据所在的地址的一个偏移量。

那么为什么我们要这样做呢?这样做有什么意义呢?
请看本节内容菱形继承的问题及解决方法,关注菱形继承中的内存变化。

面试小测:
class A {} sizeof(A) = 1
class B {} sizeof(B) = 1


class A { virtual void fun() }
class B: public A {}
sizeof(B) = 4 因为里面多了一个vfptr


class A { virtual void fun() }
class B: virtual public A {}
sizeof(B) = 8 因为里面多了一个 vbptr 和 vfptr

Windows下虚基类导致的问题:内存泄漏

一下问题仅存在于windows vs编译器。
在Linux g++不存在该问题,g++下在释放内存时会自动完成偏移释放正确的内存地址

我们考虑这样一个情况,我们重载了类A和类B的new 和 delete函数,目的就是为了查看开辟和释放内存的具体情况:

class A {
public:
    virtual void func() { cout << "Base::func" << endl;}
    void operator delete(void *ptr) {
        cout << "operator delete p : " << ptr << endl;
        free(ptr);
    }
private:
    int ma;
};
class B : virtual public A {
public:
    void func() { cout << "call B::func" << endl;}
    void* operator new( size_t size) {
        void *p = malloc(size);
        cout << "operator new p: " << p << endl;
        return p;
    }
private:
    int mb;
};

测试函数如下:

int main () {
    A *p = new B();
    cout << "main p : " << p << endl;
    p->func();
    delete p;
    return 0;
}

我们可以看到打印结果:

operator new p: 0x013855D0
main p : 0x013855D8
call B::func
operator delete p : 013855D8

我们完整讲解一下:
首先,我们在堆内存上开辟了空间,位置在:0x137606ce0
然而,我们的p指针指向的内存地址在0x147606cf0
所以我们最后释放p指针的时候,也是释放了0x147606cf0,也就是说我们有8(不同电脑上或为16字节)个字节的内存泄漏。

这是因为,我们通过A *p = new B()的p指针指向的永远是派生类内存布局中基类的首地址,现在你因为是虚基类,所以A的地址移动到后面去了,所以P指针也移动到后面去了。

总结:
我们一定不能将虚基类的指针指向一个堆内存

代码应该这么写:

int main () 
{
	B b;
	A *p = &b; //new B();
	p->func();
	//delete p;
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/637404.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

精选ThingsBoard物联网关

Thingsboard网关BL104 ThingsBoard物联网网关是一个指可以将数据采集转发至ThingsBoard平台的物联网网关&#xff0c;它使用且仅使用MQTT协议与ThingsBoard平台&#xff08;ThingsBoard IoT Platform&#xff09;进行通讯。这个网关为ThingsBoard平台提供支持更多协议的数据收…

poi操作word模板,对原有的word修改

/*** 化工园区调查表** param templatePath* param outPath* param parkInterview*/public static String getDocx(String templatePath, String outPath, ParkInterview parkInterview){File file new File(templatePath);File file1 new File(outPath);if(!file1.exists()…

2024 年 电工杯(B题)大学生数学建模挑战赛 | 平衡膳食食谱 | 数学建模完整代码+建模过程全解全析

当大家面临着复杂的数学建模问题时&#xff0c;你是否曾经感到茫然无措&#xff1f;作为2022年美国大学生数学建模比赛的O奖得主&#xff0c;我为大家提供了一套优秀的解题思路&#xff0c;让你轻松应对各种难题。 CS团队倾注了大量时间和心血&#xff0c;深入挖掘解决方案。通…

怎么查看公网IP?

在网络通信中&#xff0c;每个设备都会被分配一个IP地址&#xff0c;用于在互联网上进行唯一标识和通信。公网IP是指可以被公开访问的IP地址&#xff0c;可以用来建立远程连接或者进行网络访问等操作。怎么查看公网IP呢&#xff1f;下面将介绍几种常用的方法。 使用命令行查询公…

《中国企业报》集团数字产业发展研究院介绍

《中国企业报》集团数字产业发展研究院&#xff08;以下简称“中企数研院”&#xff09;&#xff0c;隶属于《中国企业报》集团管理。“中企数研院”致力于“数字经济产业化发展战略”大背景下&#xff0c;以“县域数字经济”、“企业数字化转型”及“数字人民币”推广等发展方…

图像超分辨率重建相关概念、评价指标、数据集、模型

1、图像超分辨率概念 1.1 基本定义 超分辨率&#xff08;Super-Resolution&#xff09;&#xff0c;简称超分&#xff08;SR&#xff09;。是指利用光学及其相关光学知识&#xff0c;根据已知图像信息恢复图像细节和其他数据信息的过程&#xff0c;简单来说就是增大图像的分辨…

springboot 配置动态调整profiles-active参数

配置动态调整active参数&#xff1a; 1.bootstrap.yml中&#xff1a; spring:profiles:active: spring.profiles.active #占位符 替换 2.pom.xml中配置&#xff1a; <build><resources><resource><directory>src/main/resources</directory>&…

牛马真的沉默了,入职第一天就干活

入职第一天就干活的&#xff0c;就问还有谁&#xff0c;搬来一台N手电脑&#xff0c;第一分钟开机&#xff0c;第二分钟派活&#xff0c;第三分钟干活&#xff0c;巴适。。。。。。 打开代码发现问题不断 读取配置文件居然读取两个配置文件&#xff0c;一个读一点&#xff0c;…

MySQL的主从复制(主从数据库都是Linux版本)

概述 1.什么是主从复制 主从复制是指将主库的 DDL 和 DML 操作通过二进制日志传到从库服务器中&#xff0c;然后在从库上对这些日志重新执行&#xff08;也叫重做&#xff09;&#xff0c;从而使得从库和主库的数据保持一致。 2.主从复制作用 数据备份&#xff1a;通过主从复…

Nuxt3 项目使用 Tailwind CSS(安装、配置、插件)

目录 构建 # 安装 tailwindcss # nuxt.config配置 # 版本 使用 # 编写 # 效果 类名联想 # 预期效果图 # vscode 安装插件 # 激活拓展 # 达到预期效果 类名排序 # 预期效果图 # 安装Prettier # prettier配置 # 达到预期效果 非标准属性排序 # 自定义类名接收…

Linux安装Docker教程(实测可用)

前言 Docker是一个开源的应用容器引擎&#xff0c;它允许开发者将应用程序及其依赖打包到一个轻量级、可移植的容器中&#xff0c;然后发布到任何流行的Linux机器上。以下是对Docker的具体介绍&#xff1a; 技术起源&#xff1a;容器技术起源于程序员对于环境搭建与应用部署效…

趣味科普:智慧采煤可视化

智慧采煤可视化系统通过趣味动画展示智能采矿技术&#xff0c;包括设备运作、矿井安全和环境保护等。

应急救援前突通信保障车技术详解

一、引言 随着现代社会的高速发展&#xff0c;各种自然灾害和突发事件频发&#xff0c;对人们的生命财产安全构成了严重威胁。应急救援前突通信保障车作为一种快速响应、高效部署的通信保障装备&#xff0c;在应急救援领域发挥着越来越重要的作用。本文将从当前形势的紧迫性、…

网络安全的重要组成部分:数据库审计

数据库审计&#xff08;简称DBAudit&#xff09;以安全事件为中心&#xff0c;以全面审计和精确审计为基础&#xff0c;实时记录网络上的数据库活动&#xff0c;对数据库操作进行细粒度审计的合规性管理&#xff0c;对数据库遭受到的风险行为进行实时告警。它通过对用户访问数据…

Centos 7 上安装【Docker】

安装Docker&#xff08;Centos 7&#xff09; 1.1.卸载&#xff08;可选&#xff09; 如果之前安装过旧版本的Docker&#xff0c;可以使用下面命令卸载&#xff1a; ***注意&#xff1a;***若提示[您需要 root 权限 执行此命令]需要在命令前加上sudo yum remove docker \do…

09.自注意力机制

文章目录 输入输出运行如何运行解决关联性attention score额外的Q K V Multi-head self-attentionPositional EncodingTruncated Self-attention影像处理vs CNNvs RNN图上的应用 输入 输出 运行 链接&#xff08;Attention Is All You Need&#xff09; 如何运行 解决关联性 a…

Vue基础(1)数据绑定

一. 文本插值 普通文本可以使用双大括号 {{ }} &#xff0c;要想插入 HTML&#xff0c;需要使用 v-html 指令。 <template><h1>Message: {{ state.msg }}</h1><p>{{ state.count 1 }}</p><p>{{ state.rawHtml }}</p><p v-html…

Java编程语言,使用自定义二元组对象替代map集合实现Key-Value格式的数据存储

一、背景 url请求地址&#xff0c;后面使用&把多个请求参数拼接起来&#xff0c;例如&#xff1a;?p1v1&p2v2&p3v3 它们的key分别是p1/p2/p3&#xff0c;对应的value分别是v1/v2/v3。 也许很多人都会通过map集合来存储&#xff0c;算比较通用的做法了&#xff1…

Windows:iHasher-v0.2安装报错Windows 功能 .NET Framework 3.5

一、情景描述 我们从MSDN下载了镜像文件&#xff0c;一般要验证下载文件的HASH值是否与MSDN上发布的一致&#xff0c;确保传出过程中iso文件没有损坏。 MSDN地址&#xff1a;https://msdn.itellyou.cn/ 我从MSDN下载了Hasher工具&#xff0c;进行校验。 但是&#xff0c;安装…