在 C++ 中,struct
(结构体)是一种用户自定义的数据类型,它允许我们将不同类型的数据项(成员)组合成一个单一的实体。默认情况下,结构体的所有成员和继承都是公有的(public)。
这使得结构体特别适合用于组织相关的数据,例如一个点的坐标、一个学生的信息等。
// 定义一个表示二维点的结构体
struct Point {
int x; // x 坐标 (通常占 4 字节)
int y; // y 坐标 (通常占 4 字节)
}; // 注意:定义末尾需要分号
// 使用结构体
Point p1; // 声明一个 Point 类型的变量 p1
p1.x = 10; // 访问并设置成员 x
p1.y = 20; // 访问并设置成员 y
对象尚未实例化
struct
和 union
(联合体)都允许组合不同类型的成员,但它们在内存管理上截然不同:
union
常用于节省内存(当不同类型数据不会同时使用时)或实现类型“双关”(type punning,但不推荐)。
(假设 int 和 float 都占 4 字节)
(共享内存区域)
在 C++ 中,struct
和 class
功能上几乎等价,都可以包含数据成员、成员函数、构造函数、析构函数、继承等。最主要的区别在于**默认访问权限**和**默认继承权限**:
特性 | struct | class |
---|---|---|
默认成员访问权限 | public (公有) |
private (私有) |
默认继承权限 | public (公有继承) |
private (私有继承) |
常用语义 | 主要用于封装**数据**,成员默认可访问。适合 POD (Plain Old Data) 类型或简单的数据聚合体。 | 主要用于封装**行为和数据**,强调信息隐藏和封装,成员默认不可直接访问。 |
struct S {
int data; // 默认 public
};
S myStruct;
myStruct.data = 10; // 尝试从外部访问
class C {
int data; // 默认 private
// public: // 需要显式声明 public 区域
// void setData(int d) { data = d; }
};
C myClass;
// myClass.data = 20; // 尝试从外部访问
实践建议:虽然技术上可以混用,但通常遵循语义约定:使用 struct
表示主要关心数据集合本身,使用 class
表示需要封装、维护不变量、提供接口的对象。
C++ 的 struct
非常灵活,远不止包含简单数据成员:
一个结构体可以包含另一个结构体作为其成员。
struct Address {
std::string city;
std::string street;
};
struct Person {
std::string name;
int age;
Address addr; // 嵌套 Address 结构体
};
struct
可以像 class
一样包含成员函数(方法)。
struct Rectangle {
int width = 0; // C++11 起支持成员初始化
int height = 0;
// 成员函数 (方法)
int area() const { // const 表示此方法不修改成员变量
return width * height;
}
void resize(int w, int h) {
width = w;
height = h;
}
};
Rectangle r = {5, 3};
r.width = 5, r.height = 3
调用 r.area()
= ?
为了方便或兼容 C 风格,可以为结构体类型创建别名:
// C++98/03 风格 (常用于 C 兼容)
typedef struct Point3D {
double x, y, z;
} Vec3; // Vec3 是 Point3D 的别名
// C++11 及以后推荐使用 using
struct Color { int r, g, b; };
using RGBColor = Color; // RGBColor 是 Color 的别名
Vec3 v; // 等同于 Point3D v;
RGBColor c; // 等同于 Color c;
定义一个名为 `Student` 的结构体,包含 `std::string name`、`int id` 和 `double score` 三个成员。编写一个函数 `void printStudent(const Student& s)`,用于打印学生的所有信息。
定义一个名为 `Value` 的联合体,包含 `int i`、`double d` 和 `char c` 三个成员。编写代码,先后向 `i`, `d`, `c` 写入值,并观察每次写入后,尝试读取其他成员会得到什么结果(可能是不确定的值)。
sizeof(MyStruct)
可能大于其所有成员大小的简单总和。
struct Example {
char c1; // 1 byte
// 3 bytes padding?
int i; // 4 bytes
char c2; // 1 byte
// 3 bytes padding?
}; // sizeof(Example) 可能是 12 而不是 1+4+1=6
可以使用 #pragma pack
或特定编译器属性来控制对齐,但这通常不推荐,除非有特定需求(如与硬件或固定格式文件交互)。
union Data { int i; float f; };
Data d;
d.f = 3.14f;
// int x = d.i; // 读取 i 的行为未定义/实现定义!
// 必须知道最后写入的是 f
现代 C++ 推荐使用 std::variant
(C++17) 或 std::any
(C++17) 来更安全地处理类型不确定的值。
struct
还是 class
应该反映你的设计意图。
struct
表示一个主要由数据组成的、其成员逻辑上可以公开访问的简单聚合体。class
表示一个具有行为、需要维护内部状态(不变量)、并对外提供受控接口的对象,其实现细节应该被隐藏(封装)。struct
常用于定义 POD 类型。C++11 之后,POD 的概念被更精确地分解为 *Trivial* 和 *Standard-layout* 类型。可以使用类型萃取 (type traits) 如 std::is_trivial::value
和 std::is_standard_layout::value
(在
头文件) 来检查一个类型是否满足这些属性。POD 类型在底层编程、序列化、与 C 代码交互时很重要。
struct
可以精确控制(或至少预测)内存布局(特别是使用 POD 类型和可能的对齐控制),它们经常用于直接映射硬件寄存器、文件格式或网络协议数据包的结构。
struct
定义中直接为非静态成员提供默认值 (如 `int x = 0;`)。{}
进行初始化,如 Point p = {10, 20};
。C++20 甚至放宽了聚合初始化的条件。constexpr
构造函数和成员函数: 如果满足条件,struct
可以拥有 constexpr
构造函数和成员函数,允许在编译时创建和操作结构体对象。std::tuple
vs struct
: 对于固定数量、类型可能不同的数据集合,std::tuple
(来自
头文件) 提供了另一种选择。tuple
的成员通过索引 (std::get<0>(myTuple)
) 访问,而 struct
的成员通过名称访问,通常更具可读性。
恭喜你完成了 C++ 结构体的学习!回顾一下关键点:
struct
是用户定义的数据类型,用于将不同类型的数据成员聚合在一起。
public
。
struct
成员各自独立存储,union
成员共享内存。
class
默认 private
)。语义上,struct
倾向于数据聚合,class
倾向于行为封装。
sizeof
可能大于成员大小之和。