结构与联合体

在C++中,结构体(struct)、联合体(union)和类(class)都是用于组织数据的复合数据类型。它们允许我们将不同类型的变量组合在一起,从而更有效地管理数据。
结构体
结构体(struct
)是由用户自定义的数据类型,它允许你将 不同类型 的数据项 组合 在一起,形成一个有机的整体,用来描述一个复杂的实体。
想象一下,如果你要描述一个“学生”实体。一个学生有很多属性:姓名、学号、年龄、成绩等等。这些属性是相关的,它们共同描述了一个学生。如果用单独的变量来存储这些属性,会非常零散,难以管理。
这时我们就可以用结构体来描述“学生”这个实体。
定义结构体
我们可以使用 struct
关键字来定义结构体:
struct 结构体类型名称 {
// 结构体成员
数据类型1 成员变量名1;
数据类型2 成员变量名2;
...
};
需要注意的是,结构体定义的末尾有一个分号 ;
,别漏掉了。
例如,我们要描述“学生”这个实体,可以定义下面这样一个结构体:
// 定义一个名为 Student 的结构体
struct Student {
// 结构体成员
int id; // 学号
string name; // 姓名
int age; // 年龄
char gender; // 性别
};
使用结构体
现在有了一个名为 Student
的结构体类型,我们就可以像使用基本数据类型(如 int
)一样,声明结构体类型的变量。
struct Student stu1; // 定义学生1
stu1.id = 202310001;
stu1.name = "Jordan";
stu1.age = 23;
stu1.gender = 'Male';
下面是完整的代码:
#include <iostream>
using namespace std;
// 结构体类型声明
struct Student {
int id; // 学号
string name; // 姓名
int age; // 年龄
char gender; // 性别
};
int main() {
struct Student s1; // 定义学生对象1
s1.id = 202310001;
s1.name = "Jordan";
s1.age = 23;
s1.gender = 'M';
struct Student s2; // 定义学生对象2
s2.id = 202310002;
s2.name = "Alice";
s2.age = 26;
s2.gender = 'F';
cout << "学生1: " << s1.name << " " << s1.age << " " << s1.gender << endl;
cout << "学生2: " << s2.name << " " << s2.age << " " << s2.gender << endl;
return 0;
}
运行程序,结果如下:
学生1: Jordan 23 M
学生2: Alice 26 F
上面是先定义结构体(Student
),再单独创建具体的对象(s1
和 s2
);也可以直接紧跟在结构体的类型声明之后创建对象,代码如下所示:
// 结构体类型声明与创建
struct Student {
int id; // 学号
string name; // 姓名
int age; // 年龄
char gender; // 性别
} s1, s2, s[100];
上面代码在声明结构体类型的同时,创建了三个具体的结构体对象:
- s1,学生对象1
- s2,学生对象2
- s[100],含有 100 个
Student
对象的数组 s
对于结构体类型的数组,访问方式与普通的数组差不多,只是通过数组直接拿到的数组元素是结构体,还需要多一层结构才能访问到具体的数据项。
下面是一个简单的示例,演示了结构体数组的访问。
#include <iostream>
using namespace std;
struct Student {
int id; // 学号
string name; // 姓名
int age; // 年龄
char gender; // 性别
} s[100];
int main() {
int n;
cin >> n;
// 生成 n 个 Student 并赋值
for (int i = 0; i < n; i++) {
s[i].id = i + 1;
s[i].name = "name" + to_string(i + 1);
s[i].age = 10;
if (i % 2 == 0) s[i].gender = 'M';
else s[i].gender = 'F';
}
for (int i = 0; i < n; i++) {
cout << s[i].id << " " << s[i].name << " " << s[i].age << " " << s[i].gender << endl;
}
return 0;
}
当输入 n=5 时,程序运行结果如下:
1 name1 10 M
2 name2 10 F
3 name3 10 M
4 name4 10 F
5 name5 10 M
结构体指针
我们也可以使用结构体指针来访问结构体的元素,有下面两种方式定义结构体指针。
定义结构体指针
你可以用一个结构体对象的地址来创建一个指针变量(也就是结构体指针变量)。
Student s;
s.id = 1;
s.name = "Jordan";
s.age = 23;
s.gender = 'M';
Student* p = &s; // 定义了一个指向 Student 结构的指针
使用指针访问结构体成员
通过指针可以访问结构体的成员,有两种常见的方式:
- 通过解引用操作符(
*
) 访问
通过解引用操作符*,我们可以访问指针所指向的结构体实例,并使用点操作符.访问其成员:
Student* p = &s; // 定义了一个指向 Student 结构的指针
cout << "Id: " << (*p).id << ", Name: " << (*p).name << endl;
- 通过箭头操作符(
->
)访问
C++提供了一种更简便的方式,即使用箭头操作符 ->
来访问成员,它结合了解引用和访问成员的操作:
Student* p = &s; // 定义了一个指向 Student 结构的指针
cout << "Id: " << p->id << ", Name: " << p->name << endl;
联合体
联合体(union)也是一种将多个不同类型的数据组合在一起的用户定义的数据类型,但与结构体不同的是,联合体的所有成员共享相同的内存空间。这意味着在同一时间只能有一个成员占用存储空间(存储有效值)。
定义联合体
使用 union
关键字来定义联合体,语法如下:
// 定义一个名为 Data 的联合体
union Data {
// 联合体成员
int intValue;
float floatValue;
char charValue;
};
使用联合体
我们可以通过以下方式创建和使用联合体:
// 声明一个 Data 类型的联合体变量
Data data;
data.intValue = 10;
cout << data.intValue << endl;
data.floatValue = 3.14;
cout << data.floatValue << endl;
下面是一个完整的示例,展示了如何定义和使用联合体:
#include <iostream>
using namespace std;
union Data {
int intValue;
float floatValue;
char charValue;
};
int main() {
Data data;
data.intValue = 10;
cout << data.intValue << endl;
data.floatValue = 3.14;
cout << data.floatValue << endl;
// 如果这时再去访问之前的 intValue,结果是不可预测的!!!
cout << data.intValue << endl;
data.charValue = 'A';
cout << data.charValue << endl;
return 0;
}
结构体与联合体看起来很相似,但它们在内存使用和应用场景上有着显著的不同。
- 内存分配:结构体的每个成员都有自己独立的内存空间,而联合体的所有成员共享同一块内存。
- 使用场景:结构体适合用于需要同时存储和访问多个不同类型的数据的情况;联合体适合用于节省内存的场景,尤其是在同一时间只需要一个数据成员的情况下。
重要: 使用联合体时,你需要自己负责跟踪当前联合体中存储的是哪个成员的值。如果你向一个成员赋值后,又去读取另一个成员的值,这是未定义行为 (Undefined Behavior),结果是不可预测的。
类
类是 C++ 中 面向对象编程 (Object-Oriented Programming, OOP) 的核心概念。它比结构体更强大,不仅可以组合数据,还可以定义操作这些数据的行为。
你可以把类看作一个更完善的“模板”或“蓝图”,它不仅描述了对象的属性(数据成员),还描述了对象能做什么(成员函数或方法)。
如何定义类
使用 class
关键字来定义类,下面是一个完整的示例代码:
#include <bits/stdc++.h>
using namespace std;
// 定义一个名为 Dog 的类
class Dog {
private: // 私有成员,只能在类内部访问
string name;
int age;
public: // 公有成员,可以在类外部访问
// 成员函数 (方法)
// 构造函数 (用于创建对象时初始化成员)
Dog(string n, int a) {
name = n;
age = a;
cout << "一只名为 " << name << " 的狗被创建了。" << endl;
}
// 成员函数:获取名字
string getName() {
return name;
}
// 成员函数:获取年龄
int getAge() {
return age;
}
// 成员函数:执行行为
void bark() {
cout << name << " 汪汪叫!" << endl;
}
// 析构函数 (在对象销毁时执行)
~Dog() {
cout << "一只名为 " << name << " 的狗被销毁了。" << endl;
}
}; // 注意类定义结束后的分号 ;
int main() {
// 这里是使用类的地方,稍后会讲
return 0;
}
下面是关于这段代码的解释:
class Dog
:定义了一个名为 Dog
的类类型。
- 类可以包含数据成员(如
name
, age
)和成员函数(如 Dog()
, getName()
, bark()
, ~Dog()
)。
- 访问修饰符 (
public
, private
, protected
): 用来控制类成员的访问权限。
public
:公有成员,可以在类的内部和外部任何地方访问。
private
:私有成员,只能在类的内部访问。这是实现封装 (Encapsulation) 的重要手段,可以保护数据不被随意修改。
protected
:保护成员,可以在类的内部和其派生类(继承类)中访问。
- 构造函数: 与类名同名,没有返回类型。在创建类的对象时自动调用,用于初始化对象的状态。
- 析构函数: 函数名前面加
~
,与类名同名,没有返回类型和参数。在对象生命周期结束时自动调用,用于清理资源。
如何使用类
定义了类类型后,你就可以创建类的对象 (Object),也称为实例化 (Instantiation)。对象是类的具体实例。
#include <bits/stdc++.h>
using namespace std;
class Dog {
private:
string name;
int age;
public:
Dog(string n, int a) { // 构造函数
name = n;
age = a;
cout << "一只名为 " << name << " 的狗被创建了。" << endl;
}
string getName() { return name; }
int getAge() { return age; }
void bark() { cout << name << " 汪汪叫!" << endl; }
~Dog() { cout << "一只名为 " << name << " 的狗被销毁了。" << endl; } // 析构函数
};
int main() {
// 创建一个 Dog 类的对象
// 调用了构造函数 Dog("旺财", 3)
Dog myDog("旺财", 3);
// 访问公有成员函数:使用点运算符 '.'
cout << "我的狗叫: " << myDog.getName() << endl;
cout << "它的年龄是: " << myDog.getAge() << endl;
myDog.bark(); // 调用 bark 方法
// 尝试访问私有成员 (会编译错误)
// cout << myDog.age << endl; // 错误!age 是 private 的
// 对象在 main 函数结束时会被销毁,自动调用析构函数 ~Dog()
return 0;
}
小结
- 结构体 (struct): 主要用于将不同类型的数据打包成一个单一的单元。在 C++ 中,它也可以包含成员函数,但默认成员是
public
的,常用于表示纯粹的数据结构。
- 联合体 (union): 是一种特殊的结构,其成员共享同一块内存空间,用于在不同类型的数据之间节省内存。使用时需要手动管理当前哪个成员是有效的。
- 类 (class): 是面向对象编程的基础,将数据和操作数据的方法封装在一起。通过访问修饰符实现封装,默认成员是
private
的,更强调数据保护和行为定义。
在实际编程中:
- 如果你只是想简单地组合一些数据,并且希望这些数据可以方便地在外部直接访问,可以使用结构体。
- 如果你需要在不同时间存储不同类型的数据,并且希望最大程度地节省内存,可以考虑使用联合体(但要注意使用时的安全性)。
- 如果你想定义一个具有特定属性和行为的对象类型,实现封装、继承、多态等面向对象特性,那么应该使用类。
在 C++ 中,struct
和 class
在功能上非常相似,主要区别在于默认访问权限。习惯上,struct
更常用于那些主要由数据组成的类型(Plain Old Data, POD),而 class
更常用于那些包含复杂行为和需要封装的类型。