当前位置: 首页 > news >正文

【C++】结构体、enum、union回顾

文章目录

  • 结构体
    • 结构体的内存对齐
  • 位段
    • 位段的内存分配
  • 枚举类型 enum
    • 强枚举类型 enum class
    • enum的内存对齐
  • 联合体 union
    • 联合体的内存对齐
    • union的使用场景


结构体

c语音的结构体在c++中升级为类,但属性和类有点区别:

访问权限:结构体中的成员默认是公有的(public)。

继承控制:结构体也可以继承,但继承默认是公有的,且一般在设计上较少用于复杂继承结构。

构造函数与析构函数:结构体也可以有构造函数和析构函数,但通常用于更简单的数据类型,主要是数据聚合。

成员函数:结构体同样可以包含成员函数,虽然通常使用时主要存储数据。

应用场景:结构体一般用于简单的数据聚合,而不需要复杂的行为或封装

结构体的内存对齐

结构体(struct)的数据成员, 第一个数据成员存放的地址为结构体变量偏移量为0的地址处.
其他结构体成员自身对齐时, 存放的地址为有效对齐值的最小整数倍的地址处.

  • 自身对齐值 : 结构体变量里每个成员的自身大小
  • 指定对齐值 : 有宏 #pragma pack(N)指定的值, 这里面的 N一定是2的幂次方.如1, 2, 4, 8, 16等.
  • 有效对齐值 : min{ 自身对齐值, 指定对齐值 }
  • 总的对齐值 : min{ 所有成员中自身对齐值最大的, 指定对齐值 } 的整数倍.
  1. 在32位Linux主机上默认指定对齐值为4, 64位的默认对齐值为8,
  2. AMR CPU默认指定对齐值为8;
  3. vs-8

在这里插入图片描述

可以用改变默认对齐数 #pragma pack(N)

在这里插入图片描述

内存对齐的意义:
许多实际的计算机系统对基本类型数据在内存中存放的位置有限制,
它们会要求这些数据的起始地址的值是某个数k的倍数,这就是所谓的内存对齐,
注:k被称为该数据类型的对齐模数(alignment modulus)。
这种强制的要求

  1. 简化了处理器与内存之间传输系统的设计
  2. 可以提升读取数据的速度。

总结:结构体内存对齐是以 空间 换 时间
怎样即节省空间又节省时间呢? - 调整变量顺序/修改默认对齐数

位段

位段(bit field)是指在数据结构中用来表示一组位的方式。它允许在结构体中定义占用特定位数的字段,从而有效地使用内存。例如,在C/C++中,可以通过定义一个结构体中的位段来压缩数据的存储。

struct Flags 
{unsigned int flag1 : 1; // 占用1位unsigned int flag2 : 1; // 占用1位unsigned int flag3 : 1; // 占用1位unsigned int reserved : 5; // 占用5位
};

位段的内存分配

  • 位段的成员可以是int , unsigned int, signed int, char(属于整型家族)

  • 位段的空间上是按照需要以4个字节(int)或者1个字节(char)的方式来开辟的

  • 位段涉及很多不确定因素,位段是不跨平台的,注意可移植程序一个避免使用位段。

在这里插入图片描述在这里插入图片描述

位段的跨平台问题

  1. int位段被看作有符号还是无符号是不确定的
  2. 位段中最大位的数码不确定(16位 - int - 2字节,32位-int-32,如int b:17,16位机器超了)
  3. 位段中成员在内存从左向右还是从右向左,不确定
  4. 当一个结构体包含两个位段,第二个位段成员比较大,无法容纳第一个位段剩余的位是,是舍弃剩余的位还是利用,不确定

枚举类型 enum

enum(枚举)是一种用户定义的数据类型,用于定义一组具有命名的整型常量。它使代码更具可读性,便于维护。基本的enum定义如下:

enum Color 
{Red,Green,Blue
};

在这个例子中,Color是一个枚举类型,包含三个可能的值:RedGreenBlue。默认情况下,第一个值的整数值为0,后续值依次递增(Red为0,Green为1,Blue为2)。

强枚举类型 enum class

c++11对enum进行了升级, 提供了强枚举类型:enum class

enum class Color
{Red,Green,Blue
};
强枚举类型可以显式指定枚举的基础类型,默认为int,但可以使用其他整型类型(如unsigned intchar等):
enum class Color : unsigned char 
{Red,Green,Blue
};

强枚举类型相比普通的枚举有什么使用上的区别呢?

作用域控制

  • 普通枚举enum)中的枚举值在定义的枚举类型外部是可见的,容易导致命名冲突。
  • 强类型枚举enum class)的枚举值则必须使用枚举类型的作用域访问,例如 EnumType::Value,避免了在不同枚举类型之间产生名称冲突。
cpp复制代码enum Status { Ok, Error };         // Ok 和 Error 直接暴露在全局作用域中
enum class State { Ok, Error };    // Ok 和 Error 限制在 State 作用域中Status s = Ok;                     // 普通枚举直接使用 Ok
State st = State::Ok;              // 强枚举需要使用 State::Ok

类型安全

  • 普通枚举可以隐式转换为整数,这在某些情况下会导致不安全的行为,比如无意中将枚举值与整数进行比较。
  • 强类型枚举不会隐式转换为整数,需要使用 static_cast,从而避免了隐式转换带来的不安全性,并提高了代码的可读性。
cpp复制代码enum Color { Red, Green, Blue };
enum class Shade { Light, Dark };int colorValue = Red;              // 普通枚举可以直接转换为整数
int shadeValue = Shade::Light;     // 强枚举会报错,不能隐式转换
int safeShadeValue = static_cast<int>(Shade::Light); // 必须显式转换

enum的内存对齐

在 C++ 中,enum 默认的底层类型通常是 int,因此默认的内存对齐与 int 类型相同。这通常是 4 字节对齐,但具体对齐要求依赖于编译器和系统架构。

enum的大小 = 底层类型大小

#include <iostream>
using namespace std;
enum e1
{a
};
enum class e2 : long long
{b
};int main()
{cout << sizeof(e1) << endl; //4cout << sizeof(e2) << endl; //8return 0;
}

联合体 union

C++ 中,联合体union)是一种特殊的数据结构,它允许在同一块内存中存储多种不同类型的数据,但每次只能存储其中的一种。也就是说,联合体的所有成员共享同一块内存,因此它的大小>=最大成员的大小。

union Data 
{int intValue;float floatValue;char charValue;
};

联合体的内存对齐

#include <stdio.h>
union Un1
{char c[5]; //5字节 有效对齐值 : min{ 1, 8 } = 1int i;     //4字节 有效对齐值 : min{ 4, 8 } = 4//取最大,故总的为5字节//总的对齐值 = min{ max{1, 4}, 8 } = 4//5不是4的倍数,扩大为8字节
};
union Un2
{short c[7]; //14字节 有效对齐值 : min{ 2, 8 } = 2int i;      // 4字节 有效对齐值 : min{ 4, 8 } = 4//取最大,故总的为14字节//总的对齐值 = min{ max{2, 4}, 8 } = 4//14不是4的倍数,扩大为16字节
};
int main()
{// 下面输出的结果是什么?printf("%d\n", sizeof(union Un1)); //8printf("%d\n", sizeof(union Un2)); //16return 0;
}

union的使用场景

自学习union到现在, 我还从没使用过一次,一直觉得它没什么用, 直到遇到一个任务: c++实现Json类

如果读者使用过Json相关的库, 如jsoncpp、Qt里的QJson,应该直到JsonValue这个类。我以QJsonValue为例子:

QJsonValue 是 Qt 提供的 JSON 数据类型之一,用于表示 JSON 文档中的一个值。它可以表示 JSON 的基本数据类型,包括:

  • Null(空值)
  • Bool(布尔值)
  • Double(双精度浮点数)
  • String(字符串)
  • Array(数组)
  • Object(对象)
QJsonValue nullValue;               // 默认构造为 Null 类型
QJsonValue boolValue(true);         // 布尔类型
QJsonValue doubleValue(42.0);       // 数值类型
QJsonValue stringValue("Hello Qt"); // 字符串类型

问题:它是如何存储不同的类型的?

最初,我以为是为不同的类型都设置一个成员,后来看了源码后,才发现是使用了union

QJsonValue源码

    union {quint64 ui;bool b;double dbl;QStringData *stringData;QJsonPrivate::Base *base;};QJsonPrivate::Data *d; // needed for Objects and ArraysType t

我模拟的KJsonValue

    union Value{int intValue;double doubleValue;bool boolValue;std::string* stringValue;KJsonObject* kjsonObjectValue;KJsonArray* kjsonArrayValue;} m_value;

问题:为什么这里存储都是对象的指针?

在 C++ 中,union 里的成员不会自动调用构造和析构函数。这是因为 union 的所有成员共享同一块内存,编译器无法判断该调用哪个成员的构造或析构函数。这种特性给 union 带来了更灵活但也更危险的管理方式。

因此如果成员中有类对象,必须要显示调用构造和析构。

#include <iostream>
#include <string>
#include <new>  // for placement newunion ExampleUnion {int intValue;double doubleValue;std::string stringValue;// 手动管理构造和析构ExampleUnion() { new (&stringValue) std::string("Hello, union!"); } // 构造 stringValue~ExampleUnion() { stringValue.~std::string(); }                     // 析构 stringValue
};int main() {ExampleUnion example;// 使用 stringValuestd::cout << "String value: " << example.stringValue << std::endl;// 切换到 intValue 前,手动析构 stringValueexample.stringValue.~std::string();example.intValue = 42;std::cout << "Integer value: " << example.intValue << std::endl;return 0;
}

如果使用指针来代替,虽然也需要在外部构造和析构,但是更灵活,更轻松。


http://www.mrgr.cn/news/61868.html

相关文章:

  • 《C++中的魔法:实现类似 Python 的装饰器模式》
  • 系统思考—啤酒游戏经营决策沙盘
  • Vue.js(2) 基础:指令与功能概览
  • 使用 firewall-cmd 管理 Linux 防火墙
  • ctfshow(155->158)--文件上传漏洞--绕过黑名单
  • [论文阅读]SimCSE: Simple Contrastive Learning of Sentence Embeddings
  • 全面解析:轻松掌握多模态技术精髓
  • YOLOv11改进策略【注意力机制篇】| ICLR2023 高效计算与全局局部信息融合的 Sea_Attention 模块(含C2PSA二次创新)
  • 【Linux】环境ChatGLM-4-9B 模型部署
  • 消息队列-Rabbitmq(消息发送,消息接收)
  • 什么情况下会导致 RCU CPU Stall 警告?
  • 平价开放式耳机品牌推荐有哪些?五大性价比开放式耳机推荐!
  • 代码随想录算法训练营第十五天|110平衡二叉树、257二叉树的所有路径 、404左叶子之和、222完全二叉树的节点个数
  • 收藏 | 推荐15个数据可视化图表绘制网站
  • Windows on ARM编译python的sherpa-onnx库
  • 网络准入控制
  • 直播推流和拉流--系统篇
  • 【机器学习(二十二)】零代码开发之LightGBM算法-Sentosa_DSML社区版
  • ssm014基于JSP的乡镇自来水收费系统+jsp(论文+源码)_kaic
  • 需求挖掘时,深入访谈5大技巧!
  • 【话题】Midjourney与未来设计:AI绘画工具能否取代人类创造力?
  • Nature子刊丨可再生能源对电力系统天气脆弱性的影响
  • Java面试经典 150 题.P27. 移除元素(002)
  • 【C++】C++预编译头文件、基准测试benchmark
  • QT相机连接与拍照
  • threejs 实现灯光照射模型有阴影