c++菱形继承、多态与类内存模型

news/2024/11/15 9:57:19/文章来源:https://www.cnblogs.com/kongweisi/p/18202906

目录
  • 1.菱形继承
    • 1.1.菱形继承的问题
    • 1.2.解决办法
  • 2.虚函数与多态
    • 2.1.普通函数不能实现多态
    • 2.2.虚函数(子类重写)+ 父类指向子类——实现多态
    • 2.3.多态原理
  • 3.c++内存模型
  • 4.参考

1.菱形继承

先看下面的例子,SheepTuo同时继承了SheepTuo,而他们同时继承Animal
image

#include <iostream>
using namespace std;class Animal
{int mAge;
};class Sheep : public Animal {};
class Tuo : public Animal {};
class SheepTuo : public Sheep, public Tuo {};int main()
{SheepTuo st;////// 1.报错,"SheepTuo::mAge" is ambiguous,mAge成员在两个子类都存在,二义性// st.mAge = 18; ////// 2.可以声明作用域,避免成员的二义性st.Sheep::mAge = 18;st.Tuo::mAge = 100;////// 3.但是在SheepTuo类中mAge成员会复制两份,造成内存浪费return 0;
}

1.1.菱形继承的问题

  1. 共享成员二义性——增加作用域可以解决
  2. 内存复制两份-浪费——虚继承解决
    image

1.2.解决办法

#include <iostream>
using namespace std;// 虚基类
class Animal
{
public:int mAge;
};// virtual --> 虚继承
class Sheep : virtual public Animal {};
class Tuo : virtual public Animal {};
class SheepTuo : public Sheep, public Tuo {};int main()
{SheepTuo st;////// 1.不会报错了// st.mAge = 18;////// 2.这样写,下面的mAge都会是100,因此虚继承后,成员不会被复制,只在基类中有一份,子类中维护vbptr(管理不同的偏移量)指向它st.Sheep::mAge = 18;st.Tuo::mAge = 100;return 0;
}

SheepTuo继承了SheepTuo的虚基类指针vbptr,这俩的指针会指向他们的虚基类表vbtable,虚基类表中存着其vbptr的偏移量,通过偏移量可以帮助子类正确找到从虚基类继承来的数据
image

2.虚函数与多态

2.1.普通函数不能实现多态

#include <iostream>
using namespace std;class Animal
{
public:void Speak(){cout << "动物 在说话" << endl;}
};class Cat : public Animal 
{void Speak(){cout << "小猫 在说话" << endl;}
};
class Dog : public Animal 
{void Speak(){cout << "小狗 在说话" << endl;}
};// 常对象只能调用常函数
// void DoSpeak(const Animal &animal)
void DoSpeak(Animal &animal)
{animal.Speak();
}int main()
{Cat cat;DoSpeak(cat);return 0;
}

输出:动物 在说话
上面的AnimalSpeak是一个普通成员函数,虽然有继承的条件,但是编译器在编译阶段,会DoSpeak函数中把animal.Speak();animal绑定为Animal的地址,无法实现多态

2.2.虚函数(子类重写)+ 父类指向子类——实现多态

#include <iostream>
using namespace std;class Animal
{
public:virtual void Speak(){cout << "动物 在说话" << endl;}
};class Cat : public Animal 
{void Speak(){cout << "小猫 在说话" << endl;}
};
class Dog : public Animal 
{void Speak(){cout << "小狗 在说话" << endl;}
};void DoSpeak(Animal &animal)
{animal.Speak();
}int main()
{Cat cat;DoSpeak(cat);return 0;
}

输出:小猫 在说话
和2.1.节相比,只增加了virtual关键字,使得父类普通函数变为虚函数,使得DoSpeak函数中animal.Speak()的animal在运行阶段才会指向真正调用的对象,实现了多态
其运行流程先说明:对象指针->取其虚表指针->取其虚表中函数->call调用

2.3.多态原理

  • case1.普通函数的基类所占内存大小, sizeof(Animal) = 1
class Animal
{
public:void Speak(){cout << "动物 在说话" << endl;}
};

image

  • case2.虚函数的基类所占内存大小, sizeof(Animal) = 4
class Animal
{
public:virtual void Speak(){cout << "动物 在说话" << endl;}
};

image

case1和case2说明了增加virtual会在类中多用内存,增加的是虚函数指针和虚函数表

  • case3.子类继承虚基类,并且子类重写虚函数
class Animal
{
public:virtual void Speak(){cout << "动物 在说话" << endl;}
};class Cat : public Animal 
{void Speak(){cout << "小猫 在说话" << endl;}
};
  • Cat中的虚函数表发生覆盖
    image
  • 总体来看看
    image

3.c++内存模型

C++类中内存存储情况比较复杂,涉及成员数据、函数、静态成员、虚函数等情况
记录结论,详情参考C++类的内存布局
image
image

4.参考

C++类的内存布局
C++多态剖析

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

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

痞子衡嵌入式:从JLink V7.62开始优化了手动增加新MCU型号支持方法

大家好,我是痞子衡,是正经搞技术的痞子。今天痞子衡给大家分享的是JLink 7.62优化了手动增加新MCU型号支持方法。JLink 工具可以说是搞单片机开发的必备神器,JLink 包括一个硬件仿真器(分不同用途的 EDU/BASE/PLUS/WIFI/ULTRA+/PRO)和 PC 机上的驱动软件(从有迹可循的 20…

AnimationClip同步工具

用途:列出动画的第1帧与预制体GameObject当前值不同的,需要同步的可以手动同步 效果图 public struct ValueNotSameItem {public EditorCurveBinding curveBinding; //关联参数public AnimationCurve animCurve; //动画曲线public float kfValue; //动画曲线上第1帧的值publi…

鸿蒙HarmonyOS实战-Stage模型(开发卡片事件)

🚀一、开发卡片事件 HarmonyOS元服务卡片页面(Metaservice Card Page)是指在HarmonyOS系统中,用于展示元服务的页面界面。元服务是指一组提供特定功能或服务的组件,例如天气服务、音乐播放服务等。元服务卡片页面可以显示元服务的相关信息和操作选项,用户可以通过点击卡…

【论文阅读】FlexGraph: A Flexible and Efficient Distributed Framework for GNN Training

阅读思考问题: Please briefly describe how hierarchical dependency graphs are built in FlexGraph, and point out the specific stage in the NAU abstraction where this process takes place. 请简要描述在FlexGraph中如何构建分层依赖图,并指出在NAU抽象中的具体阶段…

N 年前,为了学习分库分表,我把 Cobar 源码抄了一遍

10 几年前,互联网产业蓬勃发展,相比传统 IT 企业,互联网应用每天会产生海量的数据。 如何存储和分析这些数据成为了当时技术圈的痛点,彼时,分库分表解决方案应运而生。 当时最流行的 Java 技术论坛是 javaeye ,有位淘宝的技术人员分享了一篇分库分表的文章 ,这篇文章,我…

4、Git之分支操作

4.1、分支的概述 在版本控制过程中,当需要同时推进多个任务时,可以为每个任务创建的单独分支。 虽然分支的底层实现是指针的引用,但是初学阶段可以将分支简单理解为副本,一个分支就是一个单独的副本。 使用分支,意味着从原来的主干上分离开,在分支上做的任何改动,在合并…

Spring 对 Junit4,Junit5 的支持上的运用

1. Spring 对 Junit4,Junit5 的支持上的运用 @目录1. Spring 对 Junit4,Junit5 的支持上的运用每博一文案2. Spring对Junit4 的支持3. Spring对Junit5的支持4. 总结:5. 最后:每博一文案 关于理想主义,在知乎上看到一句话:“他们并不是不懂别人口中的现实,他们只是不信,事…

Eclipase的JNnit导包报错

在使用Eclipase 创建项目时系统会自动帮我们生成一个module文件,JNnit单元测试时,记得删除自动生成的 module-info.java文件,不然会一直报错找不到 org

来玩 GitHub 啊,SSH 连接方式

Windows 11 git version 2.32.0.windows.2 GitHub 20240520 --今天找回了自己的 GitHub 账号密码,继续玩吧,再次加入 蓝星的开源软件基地。 使用邮箱注册的,找回密码也很方便。本文简要展示 按照官方文档的介绍 使用 SSH 连接 GitHub 的过程。 简述为:创建SSH密钥对 公钥注…

来玩 GitHub 啊,SSH 连接

今天找回了自己的 GitHub 账号密码,继续玩吧,再次加入 蓝星的开源软件基地。 使用邮箱注册的,找回密码也很方便。本文简要展示 按照官方文档的介绍 使用 SSH 连接 GitHub 的国产。主要文档 1、Connecting to GitHub with SSH https://docs.github.com/en/authentication/con…

ABC 354 (atcoder beginer 354) D、E、F

D检查: 1. 有可能是推导式有问题,比如-/+写错 2. x,y A、B、C、D 顺序可能搞反了 不要盲目调试,先用人眼看一下代码的情况,找一下错误很简单的找规律的题目。 很不能理解过的人,就这些。 x方向,y方向,都是4行/列,一个规律的循环。求(0,0)到(x,y)中的黑色块: 第0-3…

BUUCTF-WEB(21-25)

[HCTF 2018]admin 这道题目就是admin说明得管理员登录 那我直接创一个admin的账号但是显示已经存在了说明用户名就是admin,然后我们直接爆破,也是爆破出来密码就是123直接登录[MRCTF2020]你传你🐎呢 这个题就是文件上传,我们MIME绕过,后缀名都被ban了,我们上传.htaccess…