GESP 6级
结构体进阶
6级 · 指针/STL/复杂度
💡 为什么重要
在GESP 4级你已经学过简单的结构体(只有数据成员)。6级要求掌握构造函数(自动初始化)和运算符重载(让自定义类型像基本类型一样使用)。这些是理解 STL 容器(vector、sort等)工作原理的基础,也是面向对象编程的入门。考试中常考构造函数写法和 sort 排序自定义类型的题目。
📖 前置知识
- 基本结构体定义和使用(struct、成员访问 . 和 ->)
- 数组和 vector 的基本操作
- sort 函数的简单使用
- 引用传参的概念
🔍 构造函数:让创建对象更方便
普通结构体创建后,成员的值是随机的(垃圾值)。构造函数(Constructor)是一个特殊的成员函数,在创建对象时自动调用,用来给成员赋初始值。
打个比方:构造函数就像新房子的"精装修"——你买完房子(创建对象),装修公司自动帮你装修好(初始化成员)。
📐 构造函数语法
struct Point {
int x, y;
// 构造函数:函数名与结构体名相同,无返回类型
Point(int x, int y) : x(x), y(y) {}
};
// : x(x), y(y) 叫做"初始化列表"
// 第一个x是成员变量,括号里的x是参数
// 比在函数体内赋值更高效
int x, y;
// 构造函数:函数名与结构体名相同,无返回类型
Point(int x, int y) : x(x), y(y) {}
};
// : x(x), y(y) 叫做"初始化列表"
// 第一个x是成员变量,括号里的x是参数
// 比在函数体内赋值更高效
💻 代码详解
📝 示例1:构造函数的使用
#include <iostream>
#include <string>
using namespace std;
struct Student {
string name; // 姓名
int score; // 分数
// 构造函数:创建Student时自动调用
// 参数名可以和成员名相同,用 this 区分
Student(string name, int score)
: name(name), score(score) {
// 初始化列表: name(name) 表示把参数name赋给成员name
// 也可以写成:
// this->name = name;
// this->score = score;
}
// 默认构造函数(无参数):提供默认值
Student() : name("unknown"), score(0) {}
};
int main() {
// 方式1:有参构造
Student s1("张三", 95);
cout << s1.name << " " << s1.score << endl;
// 输出: 张三 95
// 方式2:无参构造(使用默认值)
Student s2;
cout << s2.name << " " << s2.score << endl;
// 输出: unknown 0
// 方式3:省略参数名的初始化列表
Student s3 = {"李四", 88}; // C++11统一初始化
cout << s3.name << " " << s3.score << endl;
// 输出: 李四 88
return 0;
}
📝 示例2:运算符重载(让结构体支持比较)
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
struct Point {
int x, y;
// 构造函数
Point(int x = 0, int y = 0) : x(x), y(y) {}
// 重载 < 运算符
// 这样就可以用 sort() 直接排序 Point 了
// 返回 true 表示"我比o小"
bool operator<(const Point& o) const {
// 先按x排序,x相同则按y排序
if (x != o.x) return x < o.x;
return y < o.y;
}
// 重载 == 运算符
bool operator==(const Point& o) const {
return x == o.x && y == o.y;
}
};
int main() {
vector<Point> pts = {
Point(3, 1), // 创建(3,1)
Point(1, 5), // 创建(1,5)
Point(1, 2), // 创建(1,2)
Point(2, 4) // 创建(2,4)
};
// 因为我们重载了 <,sort能直接用!
sort(pts.begin(), pts.end());
// 排序结果:(1,2), (1,5), (2,4), (3,1)
for (auto& p : pts) {
cout << "(" << p.x << "," << p.y << ") ";
}
cout << endl;
// 输出: (1,2) (1,5) (2,4) (3,1)
return 0;
}
📝 示例3:结构体中使用指针和引用
#include <iostream>
using namespace std;
struct Node {
int val;
Node *next; // 指向下一个Node的指针(链表基础)
// 构造函数
Node(int v) : val(v), next(nullptr) {
// val 初始化为v,next初始化为空(不指向任何节点)
}
};
int main() {
// 创建三个节点,串成链表
Node *a = new Node(1); // 创建节点:值=1
Node *b = new Node(2); // 创建节点:值=2
Node *c = new Node(3); // 创建节点:值=3
a->next = b; // a的next指向b:1→2
b->next = c; // b的next指向c:2→3
// 链表:1 → 2 → 3 → nullptr
// 遍历链表
Node *cur = a; // 从头开始
while (cur != nullptr) {
cout << cur->val << " → "; // 访问节点的值
cur = cur->next; // 移动到下一个节点
}
cout << "NULL" << endl;
// 输出: 1 → 2 → 3 → NULL
// 记得释放内存
delete a;
delete b;
delete c;
return 0;
}
⚠️ 易错点
- 构造函数没有返回类型:构造函数不能写
void或int,直接写函数名 - 初始化列表写错顺序:初始化列表的执行顺序取决于成员声明顺序,不是列表中的书写顺序。建议按声明顺序写
- 重载运算符忘记 const:
bool operator<(const Point& o) const最后的 const 表示不会修改this,不加会报错 - 默认构造函数与有参构造冲突:如果你写了有参构造,编译器不会再自动生成默认构造。需要自己手动加一个无参的
- 结构体中的指针成员:如果结构体有指针成员,需要注意浅拷贝问题(多个对象共享同一块内存)
🎯 练习建议
- 入门:写一个 Student 结构体,包含姓名和成绩,写构造函数,创建几个学生并打印
- 进阶:写一个 Point 结构体,重载 < 运算符,然后用 sort 排序一组点
- 挑战:写一个 Fraction(分数)结构体,重载 + 运算符实现分数加法
- 综合:定义一个 struct Node,实现简单的链表(创建、遍历、释放)