6.“自定义盒子”:结构体 (struct)
“自定义盒子”:结构体 (struct)
本章导读 (The Hook)
我们已经学习了很多种“盒子”:int 盒子装整数,double 盒子装小数,string 盒子装文本,int[] 这种“一排盒子”能装下很多同类型的整数。
但现实世界中的事物往往是复杂的。比如,要描述一位“学生”,我们需要哪些信息?
- 他的名字 (一个
string) - 他的学号 (一个
int) - 他的平均分 (一个
double)
这些数据类型各不相同。如果用我们现有的工具,我们可能需要这样定义三个独立的变量来表示一位学生:
string studentName = "李华";
int studentID = 2025001;
double studentGPA = 3.8;这样做有什么问题?如果班上有 50 个学生,我们就需要创建 50 * 3 = 150 个独立的变量!这些变量在逻辑上明明是属于同一个学生的,但在代码中却被完全分开了,管理起来极其混乱。
我们真正想要的,是能创造一种全新的、自定义的“超级盒子”。这个盒子专门用来装“学生”这种事物,它内部已经预设好了几个小隔间,分别用来放 string 类型的名字、int 类型的学号和 double 类型的 GPA。
C++ 中用来创造这种自定义数据类型的蓝图,就是 结构体 (Structure),关键字是 struct。通过 struct,我们可以把不同类型的数据“打包”在一起,形成一个有机的整体,用来描述一个真实世界中的复杂对象。
专业术语速查表 (Glossary)
点击展开/折叠本章术语表
结构体 (Structure /
struct)- 通俗比喻:一个“自定义盒子”的设计蓝图。它规定了这种新盒子由哪些不同类型的小盒子(成员)组成。
- 解释:C++ 中一种用户自定义的数据类型,它允许将多个不同类型的变量组合成一个单一的实体。
成员 (Member)
- 通俗比喻:自定义盒子内部的那些“小隔间”或“属性”。比如,“学生”这个大盒子里的“姓名”、“学号”等。
- 解释:在结构体定义中声明的变量。
实例 (Instance) / 对象 (Object)
- 通俗比喻:根据“设计蓝图”实际制造出来的一个个具体的“盒子”。
- 解释:由一个结构体(或类)类型创建出来的具体变量。
.(点运算符 / 成员访问运算符)- 通俗比喻:一把“钥匙”,用来打开一个结构体盒子,并访问它内部指定的那个“小隔间”。
- 解释:用于访问结构体或类实例的成员的运算符。
->(箭头运算符 / 成员指针访问运算符)- 通俗比喻:一把“万能钥匙”,当你只有一个指向结构体盒子的指针(地址)时,用它来访问内部的成员。
- 解释:当通过一个指向结构体(或类)的指针来访问其成员时,使用箭头运算符。
ptr->member等价于(*ptr).member。
核心概念讲解 (The Core Concept)
1. 定义一个结构体 (创建设计蓝图)
我们首先要用 struct 关键字来设计我们的新“盒子”。这个定义通常放在 main 函数之前。
语法:
struct 结构体类型名 { 数据类型1 成员名1; 数据类型2 成员名2; // ... }; // 注意!这里有一个分号代码示例:设计一个“学生”结构体
#include <iostream> #include <string> using namespace std; // 定义一个名为 Student 的结构体蓝图 struct Student { string name; int id; double gpa; }; // 不要忘记这个分号!执行完这段代码,我们并没有创建任何变量,只是告诉了编译器:“嘿,我现在发明了一种全新的数据类型,叫做
Student。任何Student类型的变量,都将包含一个string、一个int和一个double。”
2. 创建并使用结构体实例 (制造并使用盒子)
有了蓝图,我们就可以在 main 函数中制造具体的“学生”盒子了。
- 代码示例:现在,所有关于李华的信息都被整齐地打包在
// (假设 Student 结构体的定义在上面) int main() { // 1. 创建一个 Student 类型的变量 (实例),名为 studentA Student studentA; // 2. 使用点运算符 . 来访问并赋值给它的成员 studentA.name = "李华"; studentA.id = 2025001; studentA.gpa = 3.8; // 3. 访问并打印成员的值 cout << "--- 学生 A 的信息 ---" << endl; cout << "姓名: " << studentA.name << endl; cout << "学号: " << studentA.id << endl; cout << "GPA: " << studentA.gpa << endl; // 也可以在创建时直接初始化 (C++11 风格) Student studentB = {"小明", 2025002, 4.0}; cout << "\n--- 学生 B 的信息 ---" << endl; cout << "姓名: " << studentB.name << endl; return 0; }studentA这一个变量里了,逻辑非常清晰。
3. 结构体与数组、函数的结合
结构体的真正威力在于,它可以和我们之前学的所有东西完美结合。
结构体数组:我们可以创建一整排“学生盒子”!
const int CLASS_SIZE = 3; Student class1[CLASS_SIZE]; // 创建一个能容纳 3 个 Student 实例的数组 class1 = {"李华", 2025001, 3.8}; class1 = {"小明", 2025002, 4.0}; class1 = {"张伟", 2025003, 3.5}; // 使用 for 循环遍历打印全班学生的名字 for (int i = 0; i < CLASS_SIZE; i++) { cout << "班级里第 " << i+1 << " 位同学是: " << class1[i].name << endl; }将结构体作为函数参数
我们可以把一整个“学生盒子”传递给函数。// 定义一个函数,专门用来打印学生信息 // 使用常量引用传递,高效又安全 void printStudentInfo(const Student& s) { cout << "----------" << endl; cout << "姓名: " << s.name << endl; cout << "学号: " << s.id << endl; cout << "GPA: " << s.gpa << endl; cout << "----------" << endl; } int main() { Student studentC = {"王芳", 2025004, 3.9}; printStudentInfo(studentC); // 把整个 studentC“盒子”传给函数 return 0; }
4. 通过指针访问结构体成员
如果我们有一个指向结构体的指针,该如何访问它的成员呢?
- 代码示例:使用
->箭头运算符结论:Student studentD = {"赵四", 2025005, 2.5}; // 创建一个指向 studentD 的指针 Student* p_student = &studentD; // 访问成员的两种等价方式: // 方式一:先解引用,再用点运算符 (比较繁琐) cout << "通过 (*p_student).name 访问: " << (*p_student).name << endl; // 方式二:使用箭头运算符 -> (更简洁,推荐!) // p_student->name 的意思就是“访问 p_student 指针所指向的那个结构体的 name 成员” cout << "通过 p_student->name 访问: " << p_student->name << endl; // 也可以通过指针修改成员 p_student->gpa = 2.8; cout << "修改后,studentD 的 GPA 是: " << studentD.gpa << endl;ptr->member是(*ptr).member的语法糖,请始终使用更简洁的箭头->形式。
动手试试 (Try It Yourself!)
定义一个“书籍”结构体
- 创建一个名为
Book的结构体。 - 它应该包含三个成员:
title(string),author(string),和publicationYear(int)。 - 在
main函数中,创建一个Book实例,为你最喜欢的一本书填充信息,并打印出来。
- 创建一个名为
管理一个矩形
- 定义一个名为
Rectangle的结构体,包含width(double) 和height(double) 两个成员。 - 创建一个
Rectangle实例。 - 编写一个名为
calculateArea的函数,它接收一个const Rectangle&作为参数,计算并返回这个矩形的面积。 - 编写另一个名为
printRectangleInfo的函数,它接收一个const Rectangle&作为参数,打印出矩形的宽度、高度和面积。
- 定义一个名为
“管理一个矩形”参考答案 (点击展开)
#include <iostream>
using namespace std;
// 1. 定义 Rectangle 结构体
struct Rectangle {
double width;
double height;
};
// 2. 定义计算面积的函数
double calculateArea(const Rectangle& rect) {
return rect.width * rect.height;
}
// 3. 定义打印信息的函数
void printRectangleInfo(const Rectangle& rect) {
cout << "--- 矩形信息 ---" << endl;
cout << "宽度: " << rect.width << endl;
cout << "高度: " << rect.height << endl;
cout << "面积: " << calculateArea(rect) << endl; // 函数可以调用其他函数!
}
int main() {
// 创建一个 Rectangle 实例
Rectangle myRect = {10.5, 5.0};
// 调用函数来处理它
printRectangleInfo(myRect);
return 0;
}本章小结 (The Summary)
我们掌握了创造自定义数据类型的强大能力,为通向面向对象编程铺平了道路。
struct允许我们将不同类型的数据成员“打包”成一个逻辑整体。- 我们通过定义
struct来创建“蓝图”,然后创建它的**实例(对象)**来使用。 - 使用点运算符
.来访问一个结构体实例的成员:myStudent.name。 - 当通过指针访问结构体成员时,使用箭头运算符
->:p_student->name。 - 结构体可以与数组、函数等所有我们学过的概念无缝结合,极大地增强了我们组织和管理复杂数据的能力。
至此,我们已经完成了 Part 3 的所有内容,你已经掌握了 C++ 过程化和结构化编程的核心精髓。你的工具箱里已经装满了各种强大的工具。
在 Part 4 中,我们将迎来一次思维方式的巨大转变。我们将不再满足于将“数据”和“操作数据的函数”分离开来,而是要学习如何将它们真正地“封装”在一起,创造出更加智能、更加独立的“对象”。欢迎来到下一章——新的思维:什么是面向对象?
