C++(第五天----多继承、虚继承、虚函数、虚表)

一、继承对象的内存空间

构造函数调用顺序,先调用父类,再调用子类

#include<iostream>using namespace std;//基类   父类
class Base{
public: //公有权限   类的外部  类的内部 Base(){cout<<"Base()"<<endl;}Base(int a):baseA(a){cout<<"Base(int a)"<<endl;}~Base(){cout<<"~Base()"<<endl;}void setData(int a){baseA = a;}void show(){cout<<"baseA:"<<baseA<<"\t addr:"<<&baseA<<endl;}
protected: //保护权限  类的内部  子类private: //私有权限  类的内部int baseA;
};//子类  派生类
class Child: public Base
{
public: //公有权限   类的外部  类的内部 //在参数列表中指定 父类的构造函数Child():Base(100){cout<<"Child()"<<endl;}Child(int a,int b):Base(a),childA(b){cout<<"Child(int a)"<<endl;}~Child(){cout<<"~Child()"<<endl;}void show(){Base::show();cout<<"childA:"<<childA<<"\t addr:"<<&childA<<endl;}
protected: //保护权限  类的内部private: //私有权限  类的内部int childA;   };int main()
{Child mya(100,200);cout<<"mya size:"<<sizeof(mya)<<"\t addr:"<<&mya<<endl;mya.show();
}

打印结果:

gec@ubuntu:/mnt/hgfs/GZ22229/14C++/05/1-code$ ./a.out 
Base(int a)
Child(int a)
mya size:8     addr:0x7ffed6dc6630
baseA:100     addr:0x7ffed6dc6630
childA:200     addr:0x7ffed6dc6634
~Child()
~Base()

在这里插入图片描述
注意:
1、子类调用成员函数的时候会检测该成员函数在子类中是否存在,如果存在就调用自己的, 如果不存在就调用 父类的(前提是父类要有这个函数)
2、如果子类和父类存在同名函数,那么在子类中 父类的函数成员会被隐藏,默认调用的就是子类函数成员。如果要调用父类的函数成员必须添加类名和作用域。

mya.Base::showData();

练习1:
设计一个基类动物类(属性:体重,颜色,年龄 行为:跑,吃,睡),构造方法初始化属性
设计一个猫类继承动物类(属性:体重,颜色,年龄,品种 行为:跑吃睡,抓老鼠,叫)
定义一个猫对象–咖菲猫,调用猫的喊叫行为,抓老鼠行为 ,输出猫的属性

练习2:编写一个输出学生和教师数据的程序,学生数据有编号、姓名、年龄、学号和成绩;教师数据有编号、姓名、年龄、职称和部门。要求声明一个 person 类,并作为学生数据操作类 student 和教师数据操作类 teacher 的基类

#include <iostream>
#include <cstring>
//将命名空间打开
/*
设计一个基类 动物类(属性:体重、颜色、年龄 、性别    行为:吃喝拉撒)
设计一个猫类 继承动物类(属性:体重、颜色、年龄 、性别、    行为:吃喝拉撒、抓老鼠)
定义一个猫对象 ---断尾猫,调用猫的行为*/
using namespace std;
//动物类
class zoon{
public:   //公有权限zoon(int weight,int age, const char * color){this->weight = weight;this->age = age;strcpy(this->color,color);}void show(){cout << "weight:" << weight <<endl;cout << "age:"   << age <<endl;cout << "color:"  << color <<endl;}
protected://私有属性int weight;    //体重int age;         //年龄char color[256];  //颜色
};
//猫类
class cat:public zoon
{
public:   //公有权限cat(int weight,int age,const char * color,const char *type):zoon(weight,age,color) {strcpy(this->type,type);}void show(){cout << "weight:" << weight <<endl;cout << "age:"   << age <<endl;cout << "color:"  << color <<endl;cout << "type:"  << type <<endl;}void behavior(){cout << "behavior" <<endl; }
private://私有char type[256];// cat Broken_tail(10,6,"white","BrokenTail");
};
int main()
{cat mya(10,6,"white","BrokenTail");mya.show();mya.behavior();  //行为return 0;
}

二、多继承

1、格式

class 子类:继承方式  基类1,继承方式  基类2 
{变量成员函数成员
};

2、例子

#include <iostream>using namespace std;//羊类
class Sheep{public:Sheep(){cout<<"Sheep()"<<endl;}Sheep(int a):dataA(a){cout<<"Sheep(int a)"<<endl;}~Sheep(){cout<<"~Sheep()"<<endl;}void show(){cout<<"dataA value:"<<dataA<<"\t addr:"<<&dataA<<endl;}int dataA;
};//驼类
class Tuo{public:Tuo(){cout<<"Tuo()"<<endl;}Tuo(int a):dataB(a){cout<<"Tuo(int a)"<<endl;}~Tuo(){cout<<"~Tuo()"<<endl;}void show(){cout<<"dataB value:"<<dataB<<"\t addr:"<<&dataB<<endl;}int dataB;
};//羊驼类
//多继承 的时候 基类的构造函数执行的顺序 跟  继承的先后顺序有关
class SheepTuo:public Sheep,public Tuo
{
public://在构造函数的参数列表中 指定 基类的构造函数SheepTuo(int a,int b):Tuo(b),Sheep(a){cout<<"SheepTuo()"<<endl;}~SheepTuo(){cout<<"~SheepTuo()"<<endl;}//在子类中重写 基类的同名函数,那么基类中的同名函数就会自动隐藏void show(){cout<<"dataA value:"<<dataA<<"\t addr:"<<&dataA<<endl;cout<<"dataB value:"<<dataB<<"\t addr:"<<&dataB<<endl;cout<<"dataC value:"<<dataC<<"\t addr:"<<&dataC<<endl;}int dataC;
};int main()
{//派生类对象的实例化SheepTuo mya(10,20);cout<<"mya size:"<<sizeof(mya)<<"\tmya addr:"<<&mya<<endl;mya.show();//通过基类的类名显示调用//mya.Sheep::show();return 0;
}结果:
mylinux@ubuntu:/mnt/hgfs/13C++编程/04/1-code$ ./a.out 
A()
B()
C()
dataA addr:0x7fffd8f7f420 dataB addr:0x7fffd8f7f424 dataC addr:0x7fffd8f7f428
~C()
~B()
~A()

总结:

  • 多继承的时候,创建子类对象构造函数执行的顺序 羊—》驼----》羊驼 ,跟基类继承的先后顺序有关
  • 在派生类的构造函数初始化列表中指定基类的构造函数
    3、内存结构图
    在这里插入图片描述
    思考:如果多个基类中有同名的函数,那么创建子类的对象,然后进行调用,会发生什么情况?怎么解决

情况:编译错误,语法不通过
解决: 1. 在子类中重新实现函数,父类函数就会自动隐藏
2. 通过父类类名显示调用

三、多继承中的菱状继承(环状继承)

1、概念
多继承中的多级继承的时候,父类的父类出现公共的基类
2、例子
在这里插入图片描述

A--->D   C-->D   B-->A、C

这个继承会使D创建两个对象

#include <iostream>using namespace std;//动物类
class Animal{
public:Animal(){cout<<"Animal()"<<endl;}Animal(int a):dataD(a){cout<<"Animal(int a)"<<endl;}~Animal(){cout<<"~Animal()"<<endl;}int dataD;
};//羊类 --继承于 动物类
class Sheep:public Animal{public:Sheep(){cout<<"Sheep()"<<endl;}Sheep(int a):dataA(a){cout<<"Sheep(int a)"<<endl;}~Sheep(){cout<<"~Sheep()"<<endl;}void show(){cout<<"dataA value:"<<dataA<<"\t addr:"<<&dataA<<endl;}int dataA;
};//驼类
class Tuo:public Animal{public:Tuo(){cout<<"Tuo()"<<endl;}Tuo(int a):dataB(a){cout<<"Tuo(int a)"<<endl;}~Tuo(){cout<<"~Tuo()"<<endl;}void show(){cout<<"dataB value:"<<dataB<<"\t addr:"<<&dataB<<endl;}int dataB;
};//羊驼类
//多继承 的时候 基类的构造函数执行的顺序 跟  继承的先后顺序有关
class SheepTuo:public Sheep,public Tuo
{
public://在构造函数的参数列表中 指定 基类的构造函数SheepTuo(int a,int b):Tuo(b),Sheep(a){cout<<"SheepTuo()"<<endl;}~SheepTuo(){cout<<"~SheepTuo()"<<endl;}//在子类中重写 基类的同名函数,那么基类中的同名函数就会自动隐藏void show(){cout<<"dataA value:"<<dataA<<"\t addr:"<<&dataA<<endl;cout<<"dataB value:"<<dataB<<"\t addr:"<<&dataB<<endl;cout<<"dataC value:"<<dataC<<"\t addr:"<<&dataC<<endl;// cout<<"dataD value:"<<dataC<<"\t addr:"<<&dataD<<endl;}int dataC;
};int main()
{//派生类对象的实例化SheepTuo mya(10,20);cout<<"mya size:"<<sizeof(mya)<<"\tmya addr:"<<&mya<<endl;mya.show();//发生二义性问题//mya.Animal::dataD = 100;//通过基类的类名显示调用//mya.Sheep::show();return 0;
}

分析:
使用qtcreator的断点调试可看到:
在这里插入图片描述
在这里插入图片描述
内存图如下:
在这里插入图片描述
通过分析可得, 创建B myb 对象的时候,构造函数顺序为 D---->A----->D—>C---->B

思考:如果myb访问D类中的成员,会发生什么情况??

答案: 会发生歧义,编译错误
在这里插入图片描述
如何解决:使用虚拟继承

四、虚拟继承(虚基类)

1、概念
为了解决从不同途径继承来的同名的数据成员在内存中有不同的拷贝造成数据不一致问题, 将共同基类设置为虚基类。这时从不同的路径继承过来的同名数据成员在内存中就只有一个 拷贝,同一个函数名也只有一个映射。这样不仅就解决了二义性问题,也节省了内存,避免了数据不一致的问题
2、格式

class  子类名称:virtual  父类名
{
};

3、例子

#include <iostream>using namespace std;//动物类
class Animal{
public:Animal(){cout<<"Animal()"<<endl;}Animal(int a):dataD(a){cout<<"Animal(int a)"<<endl;}~Animal(){cout<<"~Animal()"<<endl;}int dataD;
};//羊类 --继承于 动物类  --虚基类  Animal
class Sheep:virtual public Animal{public:Sheep(){cout<<"Sheep()"<<endl;}Sheep(int a):dataA(a){cout<<"Sheep(int a)"<<endl;}~Sheep(){cout<<"~Sheep()"<<endl;}void show(){cout<<"dataA value:"<<dataA<<"\t addr:"<<&dataA<<endl;}int dataA;
};//驼类
class Tuo:virtual public Animal{public:Tuo(){cout<<"Tuo()"<<endl;}Tuo(int a):dataB(a){cout<<"Tuo(int a)"<<endl;}~Tuo(){cout<<"~Tuo()"<<endl;}void show(){cout<<"dataB value:"<<dataB<<"\t addr:"<<&dataB<<endl;}int dataB;
};//羊驼类
//多继承 的时候 基类的构造函数执行的顺序 跟  继承的先后顺序有关
class SheepTuo:public Sheep,public Tuo
{
public://在构造函数的参数列表中 指定 基类的构造函数SheepTuo(int a,int b):Tuo(b),Sheep(a){cout<<"SheepTuo()"<<endl;}~SheepTuo(){cout<<"~SheepTuo()"<<endl;}//在子类中重写 基类的同名函数,那么基类中的同名函数就会自动隐藏void show(){cout<<"dataA value:"<<dataA<<"\t addr:"<<&dataA<<endl;cout<<"dataB value:"<<dataB<<"\t addr:"<<&dataB<<endl;cout<<"dataC value:"<<dataC<<"\t addr:"<<&dataC<<endl;cout<<"dataD value:"<<dataC<<"\t addr:"<<&dataD<<endl;}int dataC;
};int main()
{//派生类对象的实例化SheepTuo mya(10,20);cout<<"mya size:"<<sizeof(mya)<<"\tmya addr:"<<&mya<<endl;mya.show();//发生二义性问题//mya.Animal::dataD = 100;//通过基类的类名显示调用//mya.Sheep::show();return 0;
}

现象:

A:virtual public D
C:virtual public D

在这里插入图片描述
分析:
在这里插入图片描述
总结:

  • 在多继承情况下,虚基类关键字的作用范围和继承方式关键字相同,只对紧跟其后的基类起作用。
  • 虚拟继承在创建对象的时候在子类对象中会有一个虚基类表指针,该虚基类表指针存储 虚基类 对象空间的最开始位置。
  • 类对象空间的大小 由 非静态成员数据+虚基类指针+字节对齐 组成。

五、虚函数、虚函数表

1、格式

virtual 函数返回类型 函数名(参数表) {函数体};

当一个类中有一个或多个虚拟函数,那么这个类在编译的时候就会创建一个虚函数表(简称虚表) 。虚表就是一个存放虚函数指针的表格,而这个表格的首地址存放在对象的地址开始位置。
2、例子

#include <iostream>using namespace std;class Data{
public:Data(){cout<<"Data()"<<endl;}~Data(){cout<<"~Data()"<<endl;}private://在普通的函数前面加一个 关键字virtual ---虚函数virtual void setA(int val){cout<<"setA"<<endl;//dataA = val;}virtual void setB(int val){cout<<"setB"<<endl;//dataB = val;}virtual void setC(int val){cout<<"setC"<<endl;dataC = val;}int dataA;int dataB;int dataC;private:};int main()
{Data mya;cout<<"mya size:"<<sizeof(mya)<<"mya addr:"<<&mya<<endl;//如何在类的外部 去调用类中的私有函数成员//可以将 私有函数成员 声明为 虚函数 ,通过虚表指针 去调用//mya.setA(10);// mya.setB(20);// mya.setC(30);//unsigned long 在window平台 是 4个字节  unsigned long long 是 8个字节//unsigned long 在linux平台  是 8个字节//求出虚表的地址unsigned long long *vptr =  (unsigned long long *)(*(unsigned long long*)&mya);cout<<"vptr:"<<vptr<<endl;cout<<"vptr+1:"<<vptr+1<<endl;return 0;
}

通过断点调试 可以看到在这里插入图片描述

3、注意:
1)、如果一个类中包含(一个或者多个)虚函数,那么这个类的对象中会包含一个虚表指针vptr
2)、虚表指针保存在对象内存空间的最前面
3)、虚表中存储的是 类中 虚函数的地址
4)、对象调用类中的虚函数,会查询虚表指针再去执行函数

4、虚表与虚表指针,虚基类表与虚基类表指针的区别
https://blog.csdn.net/qq_42719751/article/details/104656429

六、通过虚表指针调用虚函数

1、虚函数
在类的成员函数声明前面添加virtual,这个函数就被声明为虚函数, 那么这个类的对象中包含一个指向虚表的指针
2、虚表
存储虚函数地址
3、通过虚函数指针调用虚函数
在这里插入图片描述

#include <iostream>using namespace std;class Data{
public:Data(){}~Data(){}private:int a;//virtual关键字还可以用来修饰函数  ,称之为 虚函数virtual int test01(){cout<<__FUNCTION__<<endl;}virtual int test02(){cout<<__FUNCTION__<<endl;}int test03(){cout<<__FUNCTION__<<endl;}
};int main()
{Data d1;//d1.test01();//d1.test02();cout<<"size:"<<sizeof(Data)<<endl;//通过得到虚表中的函数的地址去调用函数//如果该函数是私有权限下的函数,能够突破类中权限的限制//1)先获取虚表的地址//*(unsigned long long*)&d1;  地址常量 0x100ff1aunsigned long long*vptr = (unsigned long long*)*(unsigned long long*)&d1;cout<<"vptr:"<<vptr<<endl;//求test01函数的地址//*vptr 等价于vptr[0]  --得到test01函数的地址--但是该地址是一个常量,必须转换成函数类型的地址typedef  int (*PFunc_t)(void);
//    PFunc_t test01_p = (PFunc_t)vptr[0];
//    test01_p();((PFunc_t)vptr[0])();//    PFunc_t test02_p = (PFunc_t)vptr[1];
//    test02_p();return 0;
}

4、总结

  • 同一个类的不同实例共用同一份虚函数表, 它们都通过一个所谓的虚函数表指针__vfptr指向该虚函数表.
  • 虚表存储在进程的数据段.rodata中,虚函数存储在代码段中的.text段中
  • 当类中有虚函数的时候,编译器在编译的时候就会为该类创建一张虚表,而且只存在一份。定义类对象时, 编译器自动将类对象的__vfptr指向这个虚函数表。
  • 通过虚函数表能够突破虚函数访问权限的控制。比如我们可以将虚函数定义为private类型,这样在类外面就不能通过对象调用private类型的函数了。但是在类的虚函数表中,不论其访问类型是public还是private的,其所有虚函数都会存放其中。

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

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

相关文章

ANN文献综述

人工神经网络文献综述 摘要 人工神经网络&#xff08;Artificial Neural Networks, ANNs&#xff09;是由多个简单的、相互连接的处理单元组成的自适应系统&#xff0c;通过调整这些单元之间的连接强度&#xff0c;ANNs能够实现对复杂数据的建模和预测。本文综述了ANNs的基本…

el-input-number 点击加减只能加一次

el-input-number 点击加减只能加一次 <el-input-number v-model"editForm.quantity" placeholder"请输入下单数量(店均)" change"quantityChangeFn"></el-input-number>需要在方法里面加 this.$forceUpdate() quantityChangeFn(val…

oracle用户过期/设置无限期用户/ORA-28001:the password has expired

oracle默认情况下&#xff0c;新建的账户只有180天的有效期&#xff0c;在有效期到期前一周就会报警。而一旦过了有效期&#xff0c;账户就会被锁定无法登录。所以为了方便起见&#xff0c;要修改oracle用户的有效期为无限。 1.查看用户密码的有效期设置&#xff0c;一般默认的…

【ABB】控制器语言切换

【ABB】控制器语言切换 操作流程演示 操作流程 点击【菜单】点击【Control Panel】点击【Language】点击【Chinese】点击【OK】此时会弹出弹窗&#xff0c;点击【YES】此时控制器会重启&#xff0c;重启完成就是中文了 演示 点击【菜单】 点击【Control Panel】 点击【Langua…

【hive】数据采样

参考https://hadoopsters.com/how-random-sampling-in-hive-works-and-how-to-use-it-7cdb975aa8e2&#xff0c;可以直接查看原文&#xff0c;下面只是对原文进行概括和实际性能测试。 1.distribute by sort by2.测试3.map端数据过滤优化采样 在说数据采样之前&#xff0c;需要…

贪吃蛇——C语言(VS2022含源代码,及源代码zip文件)

一.游戏背景 贪吃蛇是一款在世界上盛名已久的小游戏&#xff0c;贪食蛇游戏操作简单&#xff0c;可玩性比较高。这个游戏难度最大的不是蛇长得很长的时候&#xff0c;而是开始。那个时候蛇身很短&#xff0c;看上去难度不大&#xff0c;却最容易死掉&#xff0c;因为把玩一条小…

Streaming local LLM with FastAPI, Llama.cpp and Langchain

题意&#xff1a; 使用FastAPI、Llama.cpp和Langchain流式传输本地大型语言模型 问题背景&#xff1a; I have setup FastAPI with Llama.cpp and Langchain. Now I want to enable streaming in the FastAPI responses. Streaming works with Llama.cpp in my terminal, but…

首个“可控”人物视频生成大模型--商汤Vimi:一张照片生成一分钟视频

商汤科技又整大活了&#xff0c;只需一张照片就能生成一分钟视频&#xff01; 7月4日&#xff0c;商汤发布了业内首个面向C端用户的、“可控”人物视频生成大模型产品Vimi&#xff0c;毫不夸张的说&#xff0c;视频制作者的福音来了&#xff01; Vimi有什么特别之处&#xff1…

在postman中调试supabase的API接口

文章目录 在supabase中获取API地址和key知道它的restfull风格在postman中进行的设置1、get请求调试2、post新增用户调试3、使用patch更新数据&#xff0c;不用put&#xff01;4、delete删除数据 总结 在supabase中获取API地址和key 首先登录dashboard后台&#xff0c;首页- 右…

特征缩放介绍

目录 一、引入特征缩放&#xff1a;二、特征缩放介绍&#xff1a;三、如何实现特征缩放&#xff1a;1.分别除特征中最大值缩放到0—1&#xff1a;2.均值归一化缩放到-1—1&#xff1a;3.Z-Score归一化&#xff1a; 四、特征缩放合理范围&#xff1a; 一、引入特征缩放&#xff…

Zabbix 配置 VMware 监控

Zabbix监控VMware 官方文档&#xff1a;https://www.zabbix.com/documentation/current/en/manual/vm_monitoring Zabbix 可以使用低级发现规则自动发现 VMware 虚拟机管理程序和虚拟机&#xff0c;并根据预定义的主机原型创建主机来监控它们。Zabbix 还包括用于监控 VMware …

精准调整:数控切割机导轨的水平与垂直度校准!

滚柱导轨因其具有高承载、高精度、高稳定性和长寿命等特点&#xff0c;被广泛应用在重型设备、精密设备、自动化生产线、航空航天和半导体设备等领域。尤其是在数控切割机中的应用&#xff0c;最为广泛。 对于数控切割机来说&#xff0c;滚柱导轨的调整非常重要&#xff0c;是数…

文本编辑新境界!轻松一键,从表格中提取特定列并保存为TXT文本

在数字化办公的时代&#xff0c;表格数据的处理是每位职场人士必须面对的任务。然而&#xff0c;面对繁杂的表格数据和海量的信息&#xff0c;如何快速准确地提取我们所需的特定列内容&#xff0c;成为了许多人头疼的问题。今天&#xff0c;就让我来为大家分享一个高效编辑的新…

一对一服务,定制化小程序:NetFarmer助力企业精准触达用户

在当今这个日新月异的数字化时代&#xff0c;小程序以其独特的魅力和广泛的应用场景&#xff0c;正逐步成为企业出海战略中的璀璨明星。NetFarmer&#xff0c;作为业界领先的数字化出海服务商&#xff0c;不仅深谙HubSpot营销自动化的精髓&#xff0c;更在小程序领域展现了卓越…

mysql 字符集(character set)和排序规则(collation)

文章目录 概念1、字符集1.1、举例1.2、常见字符集 utf8 和 utf8mb4 区别1.3、字符集 使用 2、排序规则2.1、举例2.2、常见的排序规则 utf8mb4_bin 、utf8mb4_general_ci、utf8mb4_unicode_ci2.3、使用 概念 在 MySQL 中&#xff0c;字符集&#xff08;character set&#xff0…

JAVA 对象存储OSS工具类(腾讯云)

对象存储OSS工具类 import com.qcloud.cos.COSClient; import com.qcloud.cos.ClientConfig; import com.qcloud.cos.auth.BasicCOSCredentials; import com.qcloud.cos.auth.COSCredentials; import com.qcloud.cos.model.ObjectMetadata; import com.qcloud.cos.model.PutObj…

SpringBoot的在线教育平台-计算机毕业设计源码68562

摘要 在数字化时代&#xff0c;随着信息技术的飞速发展&#xff0c;在线教育已成为教育领域的重要趋势。为了满足广大学习者对于灵活、高效学习方式的需求&#xff0c;基于Spring Boot的在线教育平台应运而生。Spring Boot以其快速开发、简便部署以及良好的可扩展性&#xff0c…

LeetCode 算法:二叉树的最近公共祖先 III c++

原题链接&#x1f517;&#xff1a;二叉树的最近公共祖先 难度&#xff1a;中等⭐️⭐️ 题目 给定一个二叉树, 找到该树中两个指定节点的最近公共祖先。 百度百科中最近公共祖先的定义为&#xff1a;“对于有根树 T 的两个节点 p、q&#xff0c;最近公共祖先表示为一个节点…

扫地机器人如何利用图算法来进行避障策略和优化清扫路径的?

前言 扫地机器人是现代家庭中最常见的智能设备。其基本的核心组件由主控系统&#xff08;大脑&#xff09;、传感器等控制系统&#xff08;感知系统&#xff09;、动力供应系统&#xff08;心脏&#xff09;、清扫系统&#xff08;四肢&#xff09;组成。 扫地机器人的智能、高…

嵌入式UI开发-lvgl+wsl2+vscode系列:6、布局(Layouts)

一、前言 这节总结一下整体页面的布局方式&#xff0c;lvgl的布局方式比较少&#xff0c;目前只有flex和grid两大类布局&#xff0c;即弹性布局和网格布局&#xff0c;弹性布局一般就是指定相对位置&#xff0c;网格布局就是将整个页面划分为网格状&#xff0c;我们做其它的UI…