【C++随笔01】联合体union —— 一种节省空间的类

【C++随笔01】联合体union —— 一种节省空间的类

  • 一、联合体(union)
  • 二、定义
  • 三、用法
    • 1、定义union、访问union成员
    • 2、匿名union
    • 3、使用类管理union成员
    • 4、管理并销毁string

一、联合体(union)

  • 联合体是一种特殊的类。一个union可以有多个数据成员,但是在任意时刻只有一个数据成员可以有值。
  • 在c++新标准中,含有构造函数或析构函数的类型也可以作为union的成员类型。
  • union的成员都是公有的。与struct相同。

当然,union也可以为其成员指定public、protected、private

  • union可以定义包括构造函数和析构函数在内从成员函数。

但是,由于union既不能继承自其他类,也不能作为基类使用,所以在union中不能含有虚函数。

联合体也被叫做共用体

二、定义

  1. 定义
    union定义的语法如下:
c++
union UnionName {unionMember1 member1;unionMember2 member2;// ...
};

其中,UnionName为该union类型的名称,unionMember1、unionMember2等为union的成员,可以是任何 C++ 数据类型(包括自定义类型),也可以是数组或指针。union的成员占用同一块内存空间,它们的大小取决于成员中占用内存最大的类型。

三、用法

1、定义union、访问union成员

  • 可以使用 .和-> 运算符来访问union中的成员变量。但需要注意的是,不同的成员变量共享同一段内存,因此只能同时访问一个成员变量
// demo1
#include<iostream>
using namespace std;union nameID
{int m_id;double m_guid;char m_name[20];
};int main() 
{nameID myID;cout << "sizeof(myID) = " << sizeof(myID) << endl << endl;cout << "myID.m_id 的地址是 = " << (void*)&myID.m_id << endl;cout << "myID.m_guid 的地址是 = " << (void*)&myID.m_guid << endl;cout << "myID.m_name 的地址是 = " << (void*)&myID.m_name << endl << endl;myID.m_guid = 3;cout << "myID.m_id = " << myID.m_id << endl;cout << "myID.m_guid = " << myID.m_guid << endl;cout << "myID.m_name = " << myID.m_name << endl << endl;return 0;
}

输出

sizeof(myID) = 24


myID.m_id 的地址是 = 000000075FB2F8C8
myID.m_guid 的地址是 = 000000075FB2F8C8
myID.m_name 的地址是 = 000000075FB2F8C8


myID.m_id = 0
myID.m_guid = 3
myID.m_name =

2、匿名union

在C++11标准中,还支持匿名union的声明。这种情况下,union没有名称,其中的每个成员都可以直接访问,不需要指定union名称

// demo2
#include<iostream>
//#include<string>
using namespace std;
struct nameID
{int i;union{int m_id;double m_guid;char m_name;};
};int main() 
{nameID myID;myID.m_name = 'a';myID.m_guid = 3.0;myID.i = 8;cout << "myID.m_id = " << myID.m_id << endl;cout << "myID.m_guid = " << myID.m_guid << endl;cout << "myID.m_name = " << myID.m_name << endl << endl;cout << "myID.i = " << myID.i << endl << endl;return 0;
}

myID.m_id = 0
myID.m_guid = 3
myID.m_name =


myID.i = 8

3、使用类管理union成员

  • 对于union来说,要想构造或销毁类类型的成员必须执行非常复杂的操作,因此我们通常把含有类类型成员的union放在另一个类当中。这个类可以管理并控制与union的类类型成员有关的状态转换。
  • 我们的类将定义一个枚举类型的成员来追踪union成员的状态。
 demo3
#include<iostream>
//#include<string>
using namespace std;
class nameID
{
public:enum nameIDType{ONLY_ID,ONLY_GUID,ONLY_NAME,};nameIDType unionType;union type{int m_id;double m_guid;char m_name;}m_nameID;nameID() :unionType(ONLY_ID), m_nameID(){}
};int main() 
{nameID myID;myID.m_nameID.m_id = 8;//myID.unionType = nameID::ONLY_ID;cout << "myID.m_id = " << myID.m_nameID.m_id << endl;cout << "myID.unionType = ONLY_ID" << endl << endl;myID.m_nameID.m_name = 'a';//myID.unionType = nameID::ONLY_NAME;cout << "myID.m_name = " << myID.m_nameID.m_name << endl;cout << "myID.unionType = ONLY_NAME" << endl << endl;myID.m_nameID.m_guid = 3.0;// myID.unionType = nameID::ONLY_GUID;cout << "myID.m_guid = " << myID.m_nameID.m_guid << endl;cout << "myID.unionType = ONLY_GUID" << endl << endl;return 0;
}

myID.m_id = 8
myID.unionType = ONLY_ID


myID.m_name = a
myID.unionType = ONLY_NAME


myID.m_guid = 3
myID.unionType = ONLY_GUID

  • 下面两种写法等价,
    • type 是 union 类型的名称。在这个 union 类型中,包含了 int 类型的 m_id、double类型的 m_guid 和 char 数组类型的 m_name。
    • 同时,m_nameID 是定义在 nameID 类中的一个对象,它的类型是 type,即上述union 类型。
    • 因此,m_nameID 可以存储三种不同类型的数据:整数、浮点数或者字符串。具体是哪种类型由 nameID 类中的 enum 类型 nameIDType 定义的枚举值决定。
union type
{int m_id;double m_guid;char m_name;
}m_nameID;
union type
{int m_id;double m_guid;char m_name;
};
type m_nameID;
  • 如果不设置unionType只设置m_nameID的值,可以吗
    • 在理论上,是可以不设置 unionType 而直接设置 m_nameID 的值的。因为 union 类型的成员变量的所有数据成员共享同一个内存空间,因此可以通过访问不同的数据成员来改变存储在 union 中的值。但是如果您不设置 unionType ,则可能会在读取 m_nameID 成员的值时出现困难,因为您无法确定当前 m_nameID 成员中存储的数据类型是什么。
    • 所以建议在使用 union 类型时,尽可能的设置枚举类型或者其他方式来标识 union 类型中存储的具体数据类型,这样能够更加清晰、明确地表达程序的意图,并且能够避免由于类型错误引起的问题。

4、管理并销毁string

new (&m_name) std::string 是 C++ 中的一个用于在已分配内存上构造对象的语法,也称为“定位 new”(Placement New)。

在默认情况下,当我们使用 new 关键字创建一个对象时,C++ 会为该对象分配新的内存,然后返回其指针。但有时我们需要在已经分配的内存上构造对象,比如在使用共享内存、内存映射文件或者自定义的内存分配器分配内存时。这时,我们可以使用定位 new 进行对象的构造。

在这个代码示例中,new (&m_name) std::string 创建了一个 std::string 对象,并将其存储到 m_name 所指向的内存地址。这里用到了 C++ 中的引用语法 &,表示获取 m_name 变量的地址。由于 m_name 是 union 的一个成员变量,因此它在不同的 unionType 下所表示的含义不同,可能作为 std::string、int 或者其他类型的存储位置。通过使用定位 new,我们能够在 m_name 所表示的内存位置上创建 std::string 对象,而不必重新分配新的内存。

需要注意的是,使用定位 new 进行对象构造后,在对象生命周期结束之前,我们应该手动调用其析构函数进行销毁,否则就会导致内存泄漏问题。在这个代码示例中,我们在 union 的析构函数中根据 unionType 的值来判断是否需要手动调用 std::string 的析构函数,从而正确地销毁内存。

 demo4
#include<iostream>
#include<string>
using namespace std;class nameID {
public:enum nameIDType {ONLY_ID,ONLY_GUID,ONLY_NAME,};nameIDType unionType;union type {int m_id;double m_guid;string m_name;type() noexcept{  new(&m_name)string; }~type() {}} m_nameID;// 默认构造函数nameID() : unionType(ONLY_ID), m_nameID() {}// 含参构造函数nameID(int id) : unionType(ONLY_ID) {m_nameID.m_id = id;}nameID(double guid) : unionType(ONLY_GUID) {m_nameID.m_guid = guid;}nameID(const string& name) : unionType(ONLY_NAME) {m_nameID.m_name = name;}// 拷贝构造函数nameID(const nameID& other) : unionType(other.unionType) {switch (unionType) {case ONLY_ID:m_nameID.m_id = other.m_nameID.m_id;break;case ONLY_GUID:m_nameID.m_guid = other.m_nameID.m_guid;break;case ONLY_NAME:m_nameID.m_name = other.m_nameID.m_name;break;}}// 赋值运算符重载函数nameID& operator=(const nameID& other) {if (this != &other) {unionType = other.unionType;switch (unionType) {case ONLY_ID:m_nameID.m_id = other.m_nameID.m_id;break;case ONLY_GUID:m_nameID.m_guid = other.m_nameID.m_guid;break;case ONLY_NAME:m_nameID.m_name = other.m_nameID.m_name;break;}}return *this;}// 析构函数~nameID() {if (ONLY_NAME == unionType){m_nameID.m_name.~string();}}// 类型转换函数operator int() const {int res;switch (unionType) {case ONLY_ID:res = m_nameID.m_id;break;case ONLY_GUID:res = static_cast<int>(m_nameID.m_guid);break;case ONLY_NAME:res = 0;break;}return res;}operator double() const {double res;switch (unionType) {case ONLY_ID:res = m_nameID.m_id;break;case ONLY_GUID:res = m_nameID.m_guid;break;case ONLY_NAME:res = 0.0;break;}return res;}operator string() const {string res;switch (unionType) {case ONLY_ID:res = to_string(m_nameID.m_id);break;case ONLY_GUID:res = to_string(m_nameID.m_guid);break;case ONLY_NAME:res = m_nameID.m_name;break;}return res;}
};int main()
{nameID id1(123);nameID id2(3.14);nameID id3("Alice");cout << static_cast<int>(id1) << endl; // 输出:123cout << static_cast<double>(id2) << endl; // 输出:3.14cout << static_cast<string>(id3) << endl; // 输出:AlicenameID id4 = id3;cout << static_cast<string>(id4) << endl; // 输出:Aliceid4 = id2;cout << static_cast<double>(id4) << endl; // 输出:3.14return 0;
}

也可以看看下面的写法,

class nameID {
public:enum nameIDType {ONLY_ID,ONLY_GUID,ONLY_NAME,};nameIDType unionType;union type {int m_id;double m_guid;string* m_name;type(int id) : m_id(id) {}type(double guid) : m_guid(guid) {}type(const string& name) : m_name(new string(name)) {} // 在 type 的构造函数中创建新的 string 对象~type() { delete m_name; } // 在 type 的析构函数中释放资源} m_nameID;// 默认构造函数nameID() : unionType(ONLY_ID), m_nameID(0) {}// 含参构造函数nameID(int id) : unionType(ONLY_ID), m_nameID(id) {}nameID(double guid) : unionType(ONLY_GUID), m_nameID(guid) {}nameID(const string& name) : unionType(ONLY_NAME), m_nameID(name) {}// 拷贝构造函数nameID(const nameID& other) : unionType(other.unionType), m_nameID(other.m_nameID) {if (unionType == ONLY_NAME && m_nameID.m_name != nullptr) {m_nameID.m_name = new string(*other.m_nameID.m_name);  // 在拷贝构造函数中创建新的 string 对象}}
}

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

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

相关文章

机器人远程控制软件设计

机器人远程控制软件设计 That’s all.

【SA8295P 源码分析】23 - QNX Ethernet MAC 驱动 之 emac1_config.conf 配置文件解析

【SA8295P 源码分析】23 - QNX Ethernet MAC 驱动 之 emac1_config.conf 配置文件解析 系列文章汇总见:《【SA8295P 源码分析】00 - 系列文章链接汇总》 本文链接:《【SA8295P 源码分析】23 - QNX Ethernet MAC 驱动 之 emac1_config.conf 配置文件解析》 主要参数如下: hw_…

【前端】vscode javascript 代码片段失效问题解决

1. 文件--首选项--用户代码片段-vue.json : 添加 // { // // Place your global snippets here. Each snippet is defined under a snippet name and has a scope, prefix, body and // // description. Add comma separated ids of the languages where the snippet is app…

iShot Pro for Mac 2.3.9最新中文版

iShot Pro是一款非常优秀的Mac截图软件&#xff0c;软件非常易于操作&#xff0c;主页面还设置了学习教程&#xff0c;可以轻松玩转软件所有功能&#xff0c;并且功能非常强大&#xff0c;不仅可以实现多种截图方式&#xff0c;还可以进行标注、贴图、取色、录屏、录音、OCR识别…

mac安装lrzsz出错Command failed with exit 128: git

终端检查电脑是否安装了rz和sz which sz若报错&#xff0c;则需要下载。由于网络和代理的原因&#xff0c;以下命令会报错&#xff1a; brew install lrzsz是因为brew和git配置的代理存在冲突&#xff0c;对于无外网链接功能&#xff0c;无特殊配置的git而言&#xff0c;需要…

QT 基本对话框

包括&#xff1a; 1.标准文件对话框 dialog.h #ifndef DIALOG_H #define DIALOG_H#include <QDialog> #include <QTextCodec> #include <QLabel> #include <QLineEdit> #include <QPushButton> #include <QGridLayout> #include <QFr…

开源的密码学工具库:openssl安装在docker容器环境Linux(ubuntu18.04)

OpenSSL&#xff08;Open Secure Socket Layer&#xff09;是一个开源的密码学工具库&#xff0c;它提供了一系列的加密、解密、认证和通信安全相关的功能。OpenSSL 最初是为了支持安全的网络通信而设计的&#xff0c;但后来它的功能逐渐扩展到了许多不同的领域&#xff0c;包括…

详细整合Spring+SpringMVC+MyBatis+logback(SSM)项目

整体目录结构 表结构 pom.xml <?xml version"1.0" encoding"UTF-8"?> <project xmlns"http://maven.apache.org/POM/4.0.0"xmlns:xsi"http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation"http://maven.a…

Zabbix-6.4.4 邮箱告警SMS告警配置

目录 ​------------------------- # 邮箱告警 ---------------------------------- 1.安装mailx与postfix软件包 2.修改mailx配置文件 3. 创建文件夹 4. 编写mail-send.sh脚本 5. 将该脚本赋予执行权限 6. 进入web界面进行设置—> Alerts —> Media Types 7. 添…

ZooKeeper客户端使用与经典应用场景

概述 ZooKeeper的应用场景依赖于ZNode节点特性和Watch监听机制。 应用场景 数据发布/订阅 常用于实现配置中心&#xff0c;类似的有nacos。数据发布/订阅的一个常见的场景是配置中心&#xff0c;发布者把数据发布到ZooKeeper的一个或一系列的节点上&#xff0c;供订阅者进行…

【Java从0到1学习】09 正则表达式

1. 正则表达式概述 在编写处理字符串的程序或网页时&#xff0c;经常会有查找符合某些复杂规则的字符串的需要。正则表达式就是用于描述这些规则的工具。换句话说&#xff0c;正则表达式就是记录文本规则的代码。 正则表达式&#xff0c;又称正规表示法、常规表示法&#xff…

Redis基础概念和数据类型详解

目录 1.什么是Redis&#xff1f; 2.为什么要使用Redis&#xff1f; 3.Redis为什么这么快&#xff1f; 4.Redis的使用场景有哪些&#xff1f; 5.Redis的基本数据类型 5.1 5种基础数据类型 5.1.1 String字符串 5.1.2 List列表 5.1.3 Set集合 5.1.4 Hash散列 5.1.5 Zset有序集…

软考高级系统架构设计师系列之:论文题目类型、论文考试大纲、历年考试论文真题汇总、论文写作原则、论文写作常见问题、论文评分标准

软考高级系统架构设计师系列之:论文题目类型、论文考试大纲、历年考试论文真题汇总、论文写作原则、论文写作常见问题、论文评分标准 一、论文写作概述二、论文题目类型三、论文考试大纲1.系统建模2.软件架构设计3.系统设计4.分布式系统设计5.系统的可靠性分析与设计6.系统的安…

第8章:集成学习

个体与集成 同质&#xff1a;相同的基学习器&#xff0c;实现容易&#xff0c;但是很难保证差异性。异质&#xff1a;不同的基学习器&#xff0c;实现复杂&#xff0c;不同模型之间本来就存在差异性&#xff0c;但是很难直接比较不同模型的输出&#xff0c;需要复杂的配准方法。…

HTML浪漫动态表白代码+音乐(附源码)(二)

一. 前言 七夕马上就要到了&#xff0c;为了帮助大家高效表白&#xff0c;下面再给大家加几款实用的HTML浪漫表白代码(附源码)背景音乐&#xff0c;可用于520&#xff0c;情人节&#xff0c;生日&#xff0c;求爱表白等场景&#xff0c;可直接使用。 来吧&#xff0c;展示&am…

gor工具http流量复制、流量回放,生产运维生气

gor是一款流量复制回放工具&#xff0c;gor工具的官网&#xff1a;https://goreplay.org/ 1、对某个端口的http流量进行打印 ./gor --input-raw :8000 --output-stdout 2、对流量实时转发&#xff0c;把81端口流量转发到192.168.3.221:80端口 ./gor --input-raw :81--output-ht…

第一百三十一天学习记录:数据结构与算法基础:栈和队列(下)(王卓教学视频)

队列的表示和操作的实现 循环顺序队列是一种使用数组来实现的队列结构&#xff0c;其中头指针和尾指针表示队列的头部和尾部位置。 当队列为空时&#xff0c;头指针和尾指针都指向同一个位置&#xff0c;即数组的第一个位置。这是因为在空队列中&#xff0c;没有任何元素可以作…

【算法刷题之数组篇(1)】

目录 1.leetcode-59. 螺旋矩阵 II&#xff08;题2.题3相当于二分变形&#xff09;2.leetcode-33. 搜索旋转排序数组3.leetcode-81. 搜索旋转排序数组 II(与题目2对比理解)&#xff08;题4和题5都是排序双指针&#xff09;4.leetcode-15. 三数之和5.leetcode-18. 四数之和6.leet…

系统卡死问题分析

CPU模式 CPU Frequency Scaling (CPUFREQ) Introduction CPU频率调节设备驱动程序的功能。该驱动程序允许在运行过程中更改CPU的时钟频率。一旦CPU频率被更改,必要的电源供应电压也会根据设备树脚本(DTS)中定义的电压值进行变化。通过降低时钟速度,这种方法可以减少功耗…

第2步---MySQL卸载和图形化工具展示

第2步---MySQL卸载和图形化工具展示 1.MySQL的卸载 2.MySQL的图形化工具 2.1常见的图形化工具 SQLyog&#xff1a;简单。SQLyog首页、文档和下载 - MySQL 客户端工具 - OSCHINA - 中文开源技术交流社区 Mysql Workbench &#xff1a;MySQL :: MySQL Workbench DataGrip&…