联合体union

非受限联合体

联合体

在C++中,union 是一种特殊的数据结构,允许在同一内存位置存储不同的数据类型。union 的每个成员都从同一内存位置开始,这就意味着 union 中的所有成员共享同一块内存。

union 的语法如下:

union MyUnion {int intValue;double doubleValue;char charValue;
};

在这个例子中,MyUnion 是一个包含三个成员的 union,分别是 intValue(整数)、doubleValue(双精度浮点数)和 charValue(字符)。union 中的每个成员都从同一内存位置开始,其大小等于最大成员的大小。

代码示例:

#include <iostream>
using namespace std;union MyUnion {int intValue;double doubleValue;char charValue;
};int main() {MyUnion myUnion;myUnion.intValue = 42;cout << "intValue: " << myUnion.intValue << endl;cout << "doubleValue: " << myUnion.doubleValue << endl; // -9.25596e+61cout << "intValue: " << myUnion.intValue << endl;cout << "charValue: " << myUnion.charValue << endl; // *myUnion.doubleValue = 3.14;cout << "doubleValue: " << myUnion.doubleValue << endl;cout << "intValue: " << myUnion.intValue << endl; // 1374389535myUnion.charValue = 'A';cout << "charValue: " << myUnion.charValue << endl;cout << "intValue: " << myUnion.intValue << endl; // 1374389569return 0;
}

程序输出结果为:

intValue: 42
doubleValue: -9.25596e+61
intValue: 42
charValue: *
doubleValue: 3.14
intValue: 1374389535
charValue: A
intValue: 1374389569

在这个例子中,myUnion 的三个成员共享同一块内存。通过修改一个成员的值,其他成员的值也会受到影响。这是因为它们共享相同的内存空间

注意事项:

  • 使用 union 需要小心,因为在不同的时刻只能使用 union 中的一个成员,对于其他成员的值可能会不可预测。
  • 当你需要在不同的数据类型之间共享内存空间,或者在节省内存时需要将不同类型的数据共用一个存储位置时,union 可能是一种有用的工具。
  • 要确保使用 union 时的操作是安全的,尽量避免引发未定义行为
非受限联合体(c++11之后的union)

在C++11之前我们使用的联合体是有局限性的,主要有以下三点:

  1. 不允许联合体拥有非POD类型的成员

  2. 不允许联合体拥有静态成员

  3. 不允许联合体拥有引用类型的成员

在新的C++11标准中,取消了关于联合体对于数据成员类型的限定,规定任何非引用类型都可以成为联合体的数据成员,这样的联合体称之为非受限联合体(Unrestricted Union)

非受限联合体的使用
静态类型的成员

对于非受限联合体来说,静态成员有两种分别是静态成员变量和静态成员函数,我们来看一下下面的代码:

union Test
{int age;long id;// int& tmp = age; // errorstatic char c;static int print(){cout << "c value: " << c << endl;return 0;}
};
char Test::c;
// char Test::c = 'a';int main()
{Test t;Test t1;t.c = 'b';t1.c = 'c';t1.age = 666;cout << "t.c: " << t.c << endl;cout << "t1.c: " << t1.c << endl;cout << "t1.age: " << t1.age << endl;cout << "t1.id: " << t1.id << endl;t.print();Test::print();return 0;
}

执行程序输出的结果如下:

t.c: c
t1.c: c
t1.age: 666
t1.id: 666
c value: c
c value: c

接下来我们逐一分析一下上面的代码:

  • 第5行:语法错误,非受限联合体中不允许出现引用类型

  • 第6行:非受限联合体中的静态成员变量

  • ​ 需要在非受限联合体外部声明(第13行)或者初始化(第14行)之后才能使用

  • ​ 通过打印的结果可以发现18、19行的t和t1对象共享这个静态成员变量(和类 class/struct 中的静态成员变量的使用是一样的)。

  • 第7行:非受限联合体中的静态成员函数

  • ​ 在静态函数print()只能访问非受限联合体Test中的静态变量,对于非静态成员变量(age、id)是无法访问的。

  • ​ 调用这个静态方法可以通过对象(第27行)也可以通过类名(第28行)实现。

  • 第24、25、26行:通过打印的结果可以得出结论在非受限联合体中静态成员变量和非静态成员变量使用的不是同一块内存。

非POD类型成员

在 C++11标准中会默认删除一些非受限联合体的默认函数。比如,非受限联合体有一个非 POD 的成员,而该非 POD成员类型拥有 非平凡的构造函数,那么**非受限联合体的默认构造函数将被编译器删除。其他的特殊成员函数,例如默认拷贝构造函数、拷贝赋值操作符以及析构函数等,也将遵从此规则。**下面来举例说明:

union Student
{int id;string name;
};int main()
{Student s;return 0;
}

编译程序会看到如下的错误提示:

warning C4624: “Student”: 已将析构函数隐式定义为“已删除”
error C2280:Student::Student(void): 尝试引用已删除的函数

上面代码中的非受限联合体Student中拥有一个非POD类型的成员string namestring 类中有非平凡构造函数,因此Student的构造函数被删除(通过警告信息可以得知它的析构函数也被删除了)导致对象无法被成功创建出来。解决这个问题的办法就是由程序猿自己为非受限联合体定义构造函数,在定义构造函数的时候我们需要用到定位放置 new操作。

placement new

一般情况下,使用new申请空间时,是从系统的堆(heap)中分配空间,申请所得的空间的位置是根据当时的内存的实际使用情况决定的。但是,在某些特殊情况下,可能需要在已分配的特定内存创建对象,这种操作就叫做placement new即定位放置 new。

定位放置new操作的语法形式不同于普通的new操作:

使用new申请内存空间:Base* ptr = new Base;

使用定位放置new申请内存空间:

ClassName* ptr = new (定位的内存地址)ClassName;

我们来看下面的示例程序:

#include <iostream>
using namespace std;class Base
{
public:Base() {}~Base() {}void print(){cout << "number value: " << number << endl;}
private:int number;
};int main()
{int n = 100;Base* b = new (&n)Base;b->print();return 0;
}

程序运行输出的结果为:

number value: 100

在程序的第20行,使用定位放置的方式为指针b申请了一块内存,也就是说此时指针 b指向的内存地址和变量 n对应的内存地址是同一块(栈内存),而在Base类中成员变量 number的起始地址和Base对象的起始地址是相同的,所以打印出 number 的值为100也就是整形变量 n 的值。

最后,给大家总结一下关于placement new的一些细节:

  1. 使用定位放置new操作,既可以在栈(stack)上生成对象,也可以在堆(heap)上生成对象,这取决于定位时指定的内存地址是在堆还是在栈上。
  2. 从表面上看,定位放置new操作是申请空间,其本质是利用已经申请好的空间,真正的申请空间的工作是在此之前完成的。
  3. 使用定位放置new 创建对象时会自动调用对应类的构造函数,但是由于对象的空间不会自动释放,如果需要释放堆内存必须显示调用类的析构函数。
  4. 使用定位放置new操作,我们可以反复动态申请到同一块堆内存,这样可以避免内存的重复创建销毁,从而提高程序的执行效率(比如网络通信中数据的接收和发送)。
自定义非受限联合体构造函数

掌握了**placement new的使用,我们通过一段程序来演示一下如何在非受限联合体中自定义构造函数**:

#include <iostream>
using namespace std;class Base
{
public:void setText(string str){notes = str;}void print(){cout << "Base notes: " << notes << endl;}
private:string notes;
};union Student
{Student(){new (&name)string;}~Student() {}int id;Base tmp;string name;
};int main()
{Student s;s.name = "蒙奇·D·路飞";s.tmp.setText("我是要成为海贼王的男人!");s.tmp.print();cout << "Student name: " << s.name << endl;return 0;
}

程序打印的结果如下:

Base notes: 我是要成为海贼王的男人!
Student name: 我是要成为海贼王的男人!

我们在上面的程序里边给非受限制联合体显示的指定了构造函数和析构函数,在程序的**第31行需要创建一个非受限联合体对象,这时便调用了联合体内部的构造函数,在构造函数的第20行通过定位放置 new的方式将构造出的对象地址定位到了联合体的成员string name的地址上了,这样联合体内部其他非静态成员也就可以访问这块地址了(通过输出的结果可以看到对联合体内的tmp对象赋值,会覆盖name对象中的数据)**。

匿名的非受限联合体

一般情况下我们使用的非受限联合体都是具名的(有名字),但是我们也可以定义匿名的非受限联合体,一个比较实用的场景就是配合着类的定义使用。我们来设定一个场景:

木叶村要进行第99次人口普查,人员的登记方式如下:- 学生只需要登记所在学校的编号- 本村学生以外的人员需要登记其身份证号码- 本村外来人员需要登记户口所在地+联系方式
#include <iostream>
using namespace std;// 外来人口信息
struct Foreigner
{Foreigner(string s, string ph) : addr(s), phone(ph) {}string addr;string phone;
};// 登记人口信息
class Person
{
public:enum class Category : char { Student, Local, Foreign };Person(int num) : number(num), type(Category::Student) {}Person(string id) : idNum(id), type(Category::Local) {}Person(string addr, string phone) : foreign(addr, phone), type(Category::Foreign) {}~Person() {}void print(){cout << "Person category: " << (int)type << endl; // 打印0代表学生, 1代表Local...switch (type){case Category::Student:cout << "Student school number: " << number << endl;break;case Category::Local:cout << "Local people ID number: " << idNum << endl;break;case Category::Foreign:cout << "Foreigner address: " << foreign.addr<< ", phone: " << foreign.phone << endl;break;default:break;}}private:Category type;union{int number;string idNum;Foreigner foreign;};
};int main()
{Person p1(9527);Person p2("1101122022X");Person p3("砂隐村村北", "1301810001");p1.print();p2.print();p3.print();return 0;
}

程序输出的结果:

Person category: 0
Student school number: 9527
Person category: 1
Local people ID number: 1101122022X
Person category: 2
Foreigner address: 砂隐村村北, phone: 1301810001

根据需求我们将木叶村的人口分为了三类并通过枚举记录了下来,在Person类中添加了一个匿名的非受限联合体用来存储人口信息,仔细分析之后就会发现这种处理方式的优势非常明显:尽可能地节省了内存空间。

  • Person类可以直接访问匿名非受限联合体内部的数据成员。
  • 不使用匿名非受限联合体申请的内存空间等于 number、 idNum 、 foreign 三者内存之和。
  • 使用匿名非受限联合体之后number、 idNum 、 foreign 三者共用同一块内存。

因为不同类型的Person对象用到不同的数据, 用到两部分数据的Foreigner类型, 我们把这种类型的两种信息放到了一个结构体中, 把这个结构体类型放到union中, 需要的时候访问.

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

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

相关文章

map()的用法

JavaScript Array map() 方法 先说说这个方法浏览器的支持&#xff1a; 支持五大主流的浏览器&#xff0c; 特别注意&#xff1a;IE 9 以下的浏览器不支持&#xff0c;只支持IE 9以上的版本的浏览器 特别注意&#xff1a;IE 9 以下的浏览器不支持&#xff0c;只支持IE 9以上的…

Android超简单实现-即时更新Toast(可直接复制)

效果 实现 第一步、封装个工具类ToastUtil.class&#xff08;可直接复制拿走&#xff09; public class ToastUtil {private static Toast mToast null; // toast对象&#xff0c;用于判断是否第一次使用/*** 即时更新Toast* param content content* param message 消息内容…

Nacos作为配置中心的一些知识二

11292327 问&#xff1a;客户端发请求给Nacos服务端&#xff0c;服务端这边会进行哪些处理&#xff1f; 答&#xff1a;客户端发请求给Nacos 服务端 &#xff0c;服务端这边通过ConfigController类的309行的listener方法&#xff0c;进行处理 第一步 获取客户端请求的文件的…

39.从0到上线三天搭建个人网站(第三天)

点赞收藏加关注&#xff0c;你也能住大别墅&#xff01; 一、第三天主要工作 1.完成detail页面的开发 2.将所有数据以及部分静态资源存在uniCloud&#xff0c;为以后做管理后台做准备 3.创建云对象getData&#xff0c;在beforecreate&#xff08;&#xff09;中获取数据 4.…

SQL server 基线安全加固操作

目录 账号管理、认证授权 ELK-Mssql-01-01-01 ELK-Mssql-01-01-02 ELK-Mssql-01-01-03 ​​​​​​​ ELK-Mssql-01-01-04 ​​​​​​​ ELK-Mssql-01-01-05 ​​​​​​​ELK-Mssql-01-01-06 日志配置 ELK-Mssql-02-01-01 通信协议 ELK-Mssql-03-01-01 ​​​​​…

【MySQL】视图:简化查询

文章目录 create view … as创建视图更改或删除视图drop view 删除视图replace关键字&#xff1a;更改视图 可更新视图with check option子句&#xff1a;防止行被删除视图的其他优点简化查询减小数据库设计改动的影响使用视图限制基础表访问 create view … as创建视图 把常用…

类 —— 友元、常/静态成员函数

类 类的大小 和结构体大小求法一致。但需注意&#xff0c;普通空类也会占用 1 字节大小&#xff0c;因为普通空类可以实例化对象。 而 抽象空类占 4 字节&#xff08;32 位机中&#xff09;&#xff0c;因为抽象空类中含有虚指针&#xff08;含有虚函数的非抽象空类同理&am…

第5章 Ajax数据爬取

目录 1. 什么是Ajax1.1 实例引入1.2 基本原理发送请求解析内容渲染网页 2. Ajax分析方法2.1 分析案例2.2 过滤请求 3. Ajax分析与爬取实战3.1 爬取目标3.2 初步探索3.3 爬取列表页分析实现基础配置爬取页面内容&#xff08;获取页面的JSON内容&#xff09;爬取列表页&#xff0…

李宏毅2020机器学习课程笔记(二)- 深度学习

相关专题: 李宏毅2020机器学习资料汇总 本系列笔记: 李宏毅2020机器学习课程笔记(一)- 分类与回归李宏毅2020机器学习课程笔记(二)- 深度学习李宏毅2020机器学习课程笔记(三)- CNN、半监督、RNN文章目录 3. Deep LearningBrief Introduction of Deep Learning(P12)Ba…

【华为OD题库-056】矩阵元素的边界值-java

题目 给定一个N * M矩阵&#xff0c;请先找出M个该矩阵中每列元素的最大值&#xff0c;然后输出这M个值中的最小值 补充说明: N和M的取值范围均为: [0,100] 示例1: 输入: [[1,2],[3,4]] 输出: 3 说明: 第一列元素为:1和3&#xff0c;最大值为3 第二列元素为: 2和4&#xff0c;最…

3D点云目标检测:VoxelNex解读

VoxelNext 通用检测器 vs VoxelNext一、3D稀疏卷积模块1.1、额外的两次下采样消融实验结果代码 1.2、稀疏体素删减消融实验&#xff1a;代码 二、稀疏体素高度压缩代码 三、稀疏预测head 通用检测器 vs VoxelNext 一、3D稀疏卷积模块 1.1、额外的两次下采样 使用通用的3D spa…

保姆级大猿人中控系统搭建教程/话费充值系统/支持代理分销(12.1更新下)

前言 csdn上面是多久没更新啦&#xff0c;没啥值得写的&#xff0c;有也懒得写~ 写文章依然是那么飘逸&#xff0c;哈哈~ 最近看上了一款系统还是挺不错的&#xff0c;直接对接话费、垫费、燃气api充值的系统&#xff0c;还可以&#xff0c;市面上主流的话费中控系统&#xf…

最新发布SPAB模块,YOLOv5改进之SPAB

目录 一、原理 二、代码 三、应用到YOLOv5 一、原理 单幅图像超分辨率(SISR)是低分辨率计算机视觉中的一项重要任务,旨在从低分辨率图像中重建高分辨率图像。传统的注意机制虽然显著提高了SISR的性能,但往往导致网络结构复杂、参数过多,导致推理速度慢

python爬虫基础知识

使用python进行网络爬虫开发之前&#xff0c;我们要对什么是浏览器、什么HTML&#xff0c;HTML构成。请求URL的方法都有一个大概了解才能更清晰的了解如何进行数据爬取。 什么是浏览器&#xff1f; 网页浏览器&#xff0c;简称为浏览器,是一种用于检索并展示万维网信息资源的…

【力扣 面试题02.07链表相交】一种思路极其清晰的解法

力扣一单简单题&#xff0c;看完大佬的题解真是佩服得五体投地&#xff01; 虽是一道简单题&#xff0c;当我吭哧吭哧写了几十行后&#xff0c;看到大佬仅仅几行直接秒掉&#xff0c;只能说算法的本质还是数学&#xff0c;数学逻辑思维真是太重要了&#xff0c;有时候真得慢慢去…

layui学习笔记

中文网1: https://www.layui1.com/doc/index.html 中文网2: https://layui.dev/docs/2/ laye弹出层: https://blog.csdn.net/qq_61313896/article/details/132901112 laye弹出层: https://blog.csdn.net/gybshen/article/details/110725734 layer.alert https://blog.csd…

操作系统进程与线程篇

目录 一、进程 1.1、进程状态 1.2、进程的控制结构 1.3、进程的控制 1.4、进程的上下文切换 二、线程 2.1.线程是什么 2.2、线程与进程的比较 2.3、线程的上下文切换 2.4、线程的实现 2.5、轻量级线程 三、进程间的通信方式 3.1、管道 3.2、消息队列 3.3、共享内…

代码人生,养生有道

导言&#xff1a; 在代码的征程中&#xff0c;我们往往忽略了自身的身体健康。这次的活动&#xff0c;我们不妨一同探讨一下&#xff0c;作为程序员&#xff0c;如何通过科学的养生方式&#xff0c;告别亚健康&#xff0c;迎接更健康、更充实的人生。 工作中的挑战 繁忙的工…

Node.js与npm的准备与操作

1.下载 Node.js官网&#xff1a;Node.jsNode.js is a JavaScript runtime built on Chromes V8 JavaScript engine.https://nodejs.org/en 打开后的界面如下&#xff1a; LTS&#xff08;Long Term Support&#xff09;&#xff1a;长期支持版&#xff0c;稳定版 Current&am…

键入网址到网页显示,期间发生了什么?(计算机网络)

一、浏览器首先会对URL进行解析 下面以http://www.server.com/dir1/file1.html为例 当没有路径名时&#xff0c;就代表访问根目录下事先设置的默认文件&#xff0c;也就是 /index.html 或者 /default.html 对URL进行解析之后&#xff0c;浏览器确定了 Web 服务器和文件名&am…