首页 > 应用开发 > IT综合 > C++自定义类型:结构体、联合与枚举深度解析与实践
《C++自定义类型:结构体、联合与枚举深度解析与实践》
在C++编程中,自定义类型为数据组织和抽象提供了强大支持。结构体、联合和枚举作为基础的自定义类型,各自有着独特的应用场景和实现原理,理解它们的特性是编写高质量C++代码的关键。
**结构体的基本定义与使用**
结构体是C++中最常用的自定义类型,用于将不同类型的数据组合成一个逻辑单元:
```cpp
#include <iostream>
#include <string>
// 基本结构体定义
struct Person {
// 数据成员
std::string name;
int age;
double height;
bool isStudent;
// 成员函数(C++中结构体可以有成员函数)
void display() const {
std::cout << "姓名: " << name << std::endl;
std::cout << "年龄: " << age << std::endl;
std::cout << "身高: " << height << "cm" << std::endl;
std::cout << "是否学生: " << (isStudent ? "是" : "否") << std::endl;
}
// 设置成员值的函数
void setInfo(const std::string& n, int a, double h, bool s) {
name = n;
age = a;
height = h;
isStudent = s;
}
};
// 结构体嵌套示例
struct Address {
std::string street;
std::string city;
std::string postalCode;
void print() const {
std::cout << street << ", " << city << " " << postalCode;
}
};
struct Employee {
Person personInfo;
Address workAddress;
std::string employeeId;
double salary;
void printEmployeeInfo() const {
std::cout << "员工ID: " << employeeId << std::endl;
personInfo.display();
std::cout << "工作地址: ";
workAddress.print();
std::cout << std::endl;
std::cout << "薪资: " << salary << std::endl;
}
};
void structBasicDemo() {
// 结构体变量的声明和初始化
Person p1; // 默认初始化
p1.name = "张三";
p1.age = 25;
p1.height = 175.5;
p1.isStudent = false;
// 聚合初始化(C++11及以后)
Person p2 = {"李四", 30, 180.0, false};
// 列表初始化(推荐)
Person p3{"王五", 22, 168.5, true};
// 访问成员
p1.display();
std::cout << std::endl;
// 结构体数组
Person team[3] = {
{"成员1", 25, 175.0, false},
{"成员2", 28, 180.0, true},
{"成员3", 30, 168.0, false}
};
for (int i = 0; i < 3; ++i) {
team[i].display();
std::cout << std::endl;
}
}
```
**结构体的高级特性**
C++11及以后版本为结构体引入了许多增强特性:
```cpp
// 带构造函数的结构体
struct Point {
double x;
double y;
// 默认构造函数
Point() : x(0.0), y(0.0) {}
// 带参数的构造函数
Point(double xVal, double yVal) : x(xVal), y(yVal) {}
// 拷贝构造函数
Point(const Point& other) : x(other.x), y(other.y) {
std::cout << "拷贝构造Point" << std::endl;
}
// 移动构造函数(C++11)
Point(Point&& other) noexcept : x(std::move(other.x)), y(std::move(other.y)) {
std::cout << "移动构造Point" << std::endl;
}
// 运算符重载
Point operator+(const Point& other) const {
return Point(x + other.x, y + other.y);
}
bool operator==(const Point& other) const {
return x == other.x && y == other.y;
}
// 类型转换运算符
explicit operator std::string() const {
return "(" + std::to_string(x) + ", " + std::to_string(y) + ")";
}
// 静态成员
static Point origin() {
return Point(0, 0);
}
// 常量成员函数
double distanceFromOrigin() const {
return std::sqrt(x*x + y*y);
}
};
// 位域结构体(用于硬件编程或协议解析)
struct PacketHeader {
unsigned int version : 4; // 4位版本号
unsigned int type : 2; // 2位类型
unsigned int flags : 2; // 2位标志位
unsigned int length : 8; // 8位长度
unsigned int checksum : 16; // 16位校验和
};
void packetDemo() {
PacketHeader header;
header.version = 2;
header.type = 1;
header.flags = 3;
header.length = 128;
header.checksum = 0xABCD;
std::cout << "数据包头大小: " << sizeof(header) << " 字节" << std::endl;
}
```
**联合的基本概念与应用**
联合是一种特殊的数据结构,所有成员共享同一块内存空间:
```cpp
#include <cstring>
// 基本联合定义
union Data {
int intValue;
double doubleValue;
char stringValue[20];
// C++11起,联合可以有构造函数和成员函数
Data() : intValue(0) {} // 必须初始化一个成员
void setInt(int val) {
intValue = val;
}
void setDouble(double val) {
doubleValue = val;
}
void setString(const char* str) {
strncpy(stringValue, str, sizeof(stringValue) - 1);
stringValue[sizeof(stringValue) - 1] = '\0';
}
};
// 带类型标记的联合(推荐模式)
struct TaggedUnion {
enum class Type { INT, DOUBLE, STRING };
Type currentType;
union {
int intValue;
double doubleValue;
char stringValue[50];
};
TaggedUnion() : currentType(Type::INT) {
intValue = 0;
}
~TaggedUnion() {
// 如果当前存储的是字符串,需要显式调用析构
if (currentType == Type::STRING) {
// 对于简单类型,可以不处理
}
}
void setInt(int val) {
currentType = Type::INT;
intValue = val;
}
void setDouble(double val) {
currentType = Type::DOUBLE;
doubleValue = val;
}
void setString(const char* str) {
currentType = Type::STRING;
strncpy(stringValue, str, sizeof(stringValue) - 1);
stringValue[sizeof(stringValue) - 1] = '\0';
}
void print() const {
switch (currentType) {
case Type::INT:
std::cout << "整数: " << intValue << std::endl;
break;
case Type::DOUBLE:
std::cout << "浮点数: " << doubleValue << std::endl;
break;
case Type::STRING:
std::cout << "字符串: " << stringValue << std::endl;
break;
}
}
};
// 匿名联合(在结构体或类内部)
struct VariantValue {
enum ValueType { INT, FLOAT, CHAR };
ValueType type;
union {
int intVal;
float floatVal;
char charVal;
};
VariantValue(int v) : type(INT), intVal(v) {}
VariantValue(float v) : type(FLOAT), floatVal(v) {}
VariantValue(char v) : type(CHAR), charVal(v) {}
};
void unionDemo() {
Data data;
// 存储不同类型的数据
data.setInt(10);
std::cout << "整数: " << data.intValue << std::endl;
data.setDouble(3.14);
std::cout << "浮点数: " << data.doubleValue << std::endl;
data.setString("Hello");
std::cout << "字符串: " << data.stringValue << std::endl;
// 注意:只能正确访问最后一次赋值的成员
std::cout << "\n带类型标记的联合使用:" << std::endl;
TaggedUnion tagged;
tagged.setInt(42);
tagged.print();
tagged.setDouble(2.718);
tagged.print();
tagged.setString("联合示例");
tagged.print();
// 计算大小
std::cout << "\nData大小: " << sizeof(Data) << " 字节" << std::endl;
std::cout << "TaggedUnion大小: " << sizeof(TaggedUnion) << " 字节" << std::endl;
}
```
**枚举类型详解**
枚举提供了类型安全的常量定义方式:
```cpp
// 传统C风格枚举
enum Color {
RED, // 0
GREEN, // 1
BLUE, // 2
YELLOW = 10, // 可以指定值
PURPLE // 11
};
// 作用域枚举(C++11引入,推荐使用)
enum class TrafficLight {
RED, // TrafficLight::RED
YELLOW,
GREEN
};
// 指定底层类型的枚举
enum class FileStatus : uint8_t {
OK = 0,
NOT_FOUND = 1,
ACCESS_DENIED = 2,
IO_ERROR = 3
};
// 带操作的枚举
enum class Month : int {
JANUARY = 1, FEBRUARY, MARCH, APRIL, MAY, JUNE,
JULY, AUGUST, SEPTEMBER, OCTOBER, NOVEMBER, DECEMBER
};
// 为枚举定义操作函数
Month nextMonth(Month current) {
int next = static_cast<int>(current) + 1;
if (next > 12) next = 1;
return static_cast<Month>(next);
}
std::string monthToString(Month month) {
switch (month) {
case Month::JANUARY: return "一月";
case Month::FEBRUARY: return "二月";
case Month::MARCH: return "三月";
case Month::APRIL: return "四月";
case Month::MAY: return "五月";
case Month::JUNE: return "六月";
case Month::JULY: return "七月";
case Month::AUGUST: return "八月";
case Month::SEPTEMBER: return "九月";
case Month::OCTOBER: return "十月";
case Month::NOVEMBER: return "十一月";
case Month::DECEMBER: return "十二月";
default: return "未知";
}
}
void enumDemo() {
// 传统枚举
Color c = RED;
int colorValue = c; // 可以隐式转换为int
std::cout << "颜色值: " << colorValue << std::endl;
// 作用域枚举
TrafficLight light = TrafficLight::RED;
// int lightValue = light; // 错误:不能隐式转换
int lightValue = static_cast<int>(light); // 需要显式转换
std::cout << "交通灯值: " << lightValue << std::endl;
// 枚举使用示例
std::cout << "\n月份枚举示例:" << std::endl;
Month current = Month::JANUARY;
for (int i = 0; i < 12; ++i) {
std::cout << static_cast<int>(current) << ": "
<< monthToString(current) << std::endl;
current = nextMonth(current);
}
// 文件状态枚举示例
FileStatus status = FileStatus::OK;
switch (status) {
case FileStatus::OK:
std::cout << "文件操作成功" << std::endl;
break;
case FileStatus::NOT_FOUND:
std::cout << "文件未找到" << std::endl;
break;
case FileStatus::ACCESS_DENIED:
std::cout << "访问被拒绝" << std::endl;
break;
case FileStatus::IO_ERROR:
std::cout << "IO错误" << std::endl;
break;
}
}
```
**复合类型实战应用**
```cpp
// 数据库记录模拟
struct DatabaseRecord {
enum class RecordType { USER, PRODUCT, ORDER };
RecordType type;
uint64_t id;
time_t createdAt;
time_t updatedAt;
union {
struct {
char username[32];
char email[64];
uint8_t age;
} user;
struct {
char name[64];
double price;
uint32_t stock;
} product;
struct {
uint64_t userId;
uint64_t productId;
uint32_t quantity;
double totalPrice;
} order;
} data;
// 工厂方法
static DatabaseRecord createUser(uint64_t id, const char* username,
<"sas.j9k5.org.cn"><"vnd.j9k5.org.cn"><"dsn.j9k5.org.cn"><"abb.j9k5.org.cn">
const char* email, uint8_t age) {
DatabaseRecord record;
record.type = RecordType::USER;
record.id = id;
record.createdAt = record.updatedAt = time(nullptr);
strncpy(record.data.user.username, username,
sizeof(record.data.user.username) - 1);
strncpy(record.data.user.email, email,
sizeof(record.data.user.email) - 1);
record.data.user.age = age;
return record;
}
void print() const {
std::cout << "ID: " << id << std::endl;
std::cout << "类型: ";
switch (type) {
case RecordType::USER:
std::cout << "用户" << std::endl;
std::cout << "用户名: " << data.user.username << std::endl;
std::cout << "邮箱: " << data.user.email << std::endl;
std::cout << "年龄: " << static_cast<int>(data.user.age) << std::endl;
break;
case RecordType::PRODUCT:
std::cout << "产品" << std::endl;
std::cout << "名称: " << data.product.name << std::endl;
std::cout << "价格: " << data.product.price << std::endl;
std::cout << "库存: " << data.product.stock << std::endl;
break;
case RecordType::ORDER:
std::cout << "订单" << std::endl;
std::cout << "用户ID: " << data.order.userId << std::endl;
std::cout << "产品ID: " << data.order.productId << std::endl;
std::cout << "数量: " << data.order.quantity << std::endl;
std::cout << "总价: " << data.order.totalPrice << std::endl;
break;
}
std::cout << "创建时间: " << ctime(&createdAt);
std::cout << "更新时间: " << ctime(&updatedAt);
}
};
// 网络协议数据包解析
struct NetworkPacket {
enum class PacketType : uint8_t {
DATA = 0x01,
ACK = 0x02,
NAK = 0x03,
CONTROL = 0x04
};
struct Header {
uint16_t packetId;
PacketType type;
uint16_t length;
uint32_t checksum;
};
Header header;
// 变长数据部分
union {
struct {
uint32_t sequenceNumber;
char data[1]; // 灵活数组成员(C99特性,C++需特殊处理)
} dataPacket;
struct {
uint32_t ackNumber;
uint32_t windowSize;
} ackPacket;
struct {
uint8_t command;
uint8_t parameters[16];
} controlPacket;
};
// 创建数据包
static NetworkPacket* createDataPacket(uint16_t id, uint32_t seqNum,
const char* data, uint16_t dataLen) {
size_t packetSize = sizeof(NetworkPacket) + dataLen;
auto* packet = reinterpret_cast<NetworkPacket*>(new char[packetSize]);
packet->header.packetId = id;
packet->header.type = PacketType::DATA;
packet->header.length = sizeof(Header) + sizeof(uint32_t) + dataLen;
packet->dataPacket.sequenceNumber = seqNum;
if (dataLen > 0) {
memcpy(packet->dataPacket.data, data, dataLen);
}
// 计算校验和
packet->header.checksum = calculateChecksum(packet);
return packet;
}
private:
static uint32_t calculateChecksum(const NetworkPacket* packet) {
// 简单的校验和计算示例
uint32_t sum = 0;
const uint8_t* bytes = reinterpret_cast<const uint8_t*>(packet);
for (size_t i = 0; i < packet->header.length; ++i) {
sum += bytes[i];
}
return sum;
}
};
```
**内存布局与性能优化**
```cpp
#include <iomanip>
struct MemoryLayoutDemo {
// 未优化的结构体
struct Unoptimized {
bool flag1; // 1字节
int value; // 4字节
bool flag2; // 1字节
double amount; // 8字节
char code; // 1字节
};
// 优化后的结构体(考虑内存对齐)
struct Optimized {
double amount; // 8字节
int value; // 4字节
bool flag1; // 1字节
bool flag2; // 1字节
char code; // 1字节
// 编译器可能在这里插入填充字节
};
// 使用编译指示控制对齐
#pragma pack(push, 1) // 1字节对齐
struct Packed {
bool flag1;
int value;
bool flag2;
double amount;
char code;
};
#pragma pack(pop) // 恢复默认对齐
static void demonstrate() {
std::cout << "内存布局分析:" << std::endl;
std::cout << "=" << 30 << std::endl;
std::cout << "Unoptimized 大小: " << sizeof(Unoptimized) << " 字节" << std::endl;
std::cout << "Optimized 大小: " << sizeof(Optimized) << " 字节" << std::endl;
std::cout << "Packed 大小: " << sizeof(Packed) << " 字节" << std::endl;
// 显示成员偏移
std::cout << "\n成员偏移(以字节为单位):" << std::endl;
std::cout << std::setw(20) << "结构体"
<< std::setw(10) << "flag1"
<< std::setw(10) << "value"
<< std::setw(10) << "flag2"
<< std::setw(10) << "amount"
<< std::setw(10) << "code" << std::endl;
std::cout << std::setw(20) << "Unoptimized"
<< std::setw(10) << offsetof(Unoptimized, flag1)
<< std::setw(10) << offsetof(Unoptimized, value)
<< std::setw(10) << offsetof(Unoptimized, flag2)
<< std::setw(10) << offsetof(Unoptimized, amount)
<< std::setw(10) << offsetof(Unoptimized, code) << std::endl;
std::cout << std::setw(20) << "Optimized"
<< std::setw(10) << offsetof(Optimized, flag1)
<< std::setw(10) << offsetof(Optimized, value)
<< std::setw(10) << offsetof(Optimized, flag2)
<< std::setw(10) << offsetof(Optimized, amount)
<< std::setw(10) << offsetof(Optimized, code) << std::endl;
}
};
```
**现代C++特性应用**
```cpp
// C++17结构化绑定
struct Rectangle {
double width;
double height;
std::pair<double, double> getDimensions() const {
return {width, height};
}
};
void structuredBindingDemo() {
Rectangle rect{10.0, 20.0};
// C++17结构化绑定
auto [w, h] = rect.getDimensions();
std::cout << "宽度: " << w << ", 高度: " << h << std::endl;
// 用于枚举类型
enum class RGB { RED, GREEN, BLUE };
std::tuple<RGB, int, std::string> colorInfo{RGB::GREEN, 255, "绿色"};
<"avg.j9k5.org.cn"><"avr.j9k5.org.cn"><"evs.j9k5.org.cn">
auto [color, value, name] = colorInfo;
std::cout << "颜色: " << static_cast<int>(color)
<< ", 值: " << value
<< ", 名称: " << name << std::endl;
}
// 使用using定义类型别名(C++11)
using UserId = uint64_t;
using Timestamp = time_t;
struct UserRecord {
UserId id;
std::string name;
Timestamp registrationDate;
// 使用auto返回类型(C++14)
auto getAgeInDays() const {
time_t now = time(nullptr);
return difftime(now, registrationDate) / (60 * 60 * 24);
}
};
// constexpr结构体(C++11)
struct Circle {
double radius;
constexpr Circle(double r) : radius(r) {}
constexpr double getArea() const {
return 3.141592653589793 * radius * radius;
}
constexpr double getCircumference() const {
return 2 * 3.141592653589793 * radius;
}
};
void compileTimeDemo() {
// 编译时计算
constexpr Circle unitCircle(1.0);
constexpr double area = unitCircle.getArea();
constexpr double circumference = unitCircle.getCircumference();
std::cout << "单位圆面积: " << area << std::endl;
std::cout << "单位圆周长: " << circumference << std::endl;
}
```
**最佳实践总结**
1. **结构体使用建议**:
- 对于简单数据聚合,使用struct而非class
- 合理组织成员顺序以优化内存布局
- 为复杂结构体提供构造函数和验证逻辑
- 考虑使用constexpr实现编译时计算
2. **联合使用场景**:
- 内存敏感型应用,需要节省空间
- 协议解析和硬件寄存器访问
- 实现变体类型(配合类型标记)
- 注意:同一时刻只能使用一个成员
3. **枚举最佳实践**:
- 优先使用enum class而非传统enum
- 为枚举指定明确的底层类型
- 为枚举提供辅助函数(如toString)
- 避免枚举值隐式转换
4. **性能与安全**:
- 注意结构体填充和内存对齐
- 使用编译指示控制打包(如#pragma pack)
- 确保联合访问的安全性(使用类型标记)
- 考虑缓存友好性,将相关数据放在一起
5. **现代C++特性**:
- 使用auto和decltype简化代码
- 应用结构化绑定处理多返回值
- 利用constexpr实现编译时计算
- 使用using代替typedef定义类型别名
结构体、联合和枚举作为C++基础类型系统的延伸,为数据组织提供了强大而灵活的工具。理解它们的特性和适用场景,能够帮助开发者编写出更高效、更安全、更易维护的代码。在实际应用中,应根据具体需求选择最合适的类型,并遵循最佳实践原则。
来自 “ ITPUB博客 ” ,链接:https://blog.itpub.net/19545/viewspace-3109385/ ,如需转载,请注明出处,否则将追究法律责任。