在C++编程中,结构体(struct)是一种非常重要的复合数据类型,它允许我们将不同类型的数据组合成一个整体。对于准备GESP C++4级认证的考生来说,掌握结构体的使用是必备技能。结构体特别适合用来表示具有多个属性的实体,比如学生信息、商品信息等。
结构体的声明使用struct关键字,后面跟着结构体名称和一对花括号,花括号内是成员变量的声明。例如,我们可以这样定义一个表示学生的结构体:
cpp复制struct Student {
string name; // 姓名
int age; // 年龄
char gender; // 性别
float score; // 成绩
};
这里有几个关键点需要注意:
提示:良好的编程习惯是为结构体成员选择有意义的名称,并添加注释说明每个成员的用途。
定义结构体变量有两种主要方式:顺序初始化和指定成员初始化。
顺序初始化是按照结构体声明时成员的顺序,依次为每个成员赋值:
cpp复制Student stu1 = {"张三", 20, 'm', 94.0f};
Student stu2 = {"李四", 19, 'f', 98.5f};
这种方式简洁明了,但需要特别注意:
C++11引入了更灵活的初始化方式,可以显式指定要为哪些成员赋值:
cpp复制Student stu3 = {
.name = "王五",
.age = 21,
.score = 92.5f // gender未指定,将使用默认值
};
这种方式的优点:
注意:指定成员初始化是C++11引入的特性,在较老的编译器上可能不支持。
定义结构体变量后,我们可以使用点运算符(.)来访问其成员:
cpp复制Student stu;
stu.name = "赵六";
stu.age = 18;
stu.gender = 'm';
stu.score = 88.5f;
cout << "姓名:" << stu.name << endl;
cout << "年龄:" << stu.age << endl;
当使用指向结构体的指针时,我们需要使用箭头运算符(->)来访问成员:
cpp复制Student stu;
Student* pStu = &stu;
pStu->name = "钱七";
pStu->age = 22;
cout << "姓名:" << pStu->name << endl;
实际上,pStu->name等价于(*pStu).name,但前者更简洁直观。
结构体变量之间可以直接赋值,这会执行成员逐个拷贝:
cpp复制Student stu1 = {"张三", 20, 'm', 94.0f};
Student stu2 = stu1; // 将stu1的值拷贝给stu2
这种赋值是浅拷贝,对于包含指针成员的结构体要特别注意,可能需要自定义拷贝操作。
结构体可以作为函数的参数和返回值,传递方式有三种:
cpp复制void printStudent(Student s) {
cout << s.name << endl;
}
cpp复制void printStudent(const Student& s) {
cout << s.name << endl;
}
cpp复制void updateStudent(Student* s) {
s->age += 1;
}
提示:对于大型结构体,优先使用const引用传递,既高效又安全。
我们可以创建结构体数组来管理多个相同类型的实体:
cpp复制Student class1[30]; // 能容纳30个学生的数组
// 初始化第一个学生
class1[0] = {"张三", 20, 'm', 94.0f};
// 访问第二个学生的年龄
class1[1].age = 19;
结构体数组在需要处理大量相似数据时非常有用,比如学生成绩管理系统、员工信息管理等场景。
结构体可以包含其他结构体作为成员,这种嵌套结构可以表示更复杂的数据关系:
cpp复制struct Date {
int year;
int month;
int day;
};
struct Student {
string name;
Date birthday; // 嵌套Date结构体
float score;
};
// 初始化
Student stu = {"张三", {2003, 5, 12}, 94.0f};
// 访问嵌套成员
cout << stu.birthday.year << endl;
这类题目通常要求考生根据题目描述定义适当的结构体,并进行初始化。例如:
"定义一个表示图书的结构体Book,包含书名(string)、作者(string)、价格(float)和库存量(int)成员。然后创建并初始化两个Book变量。"
解答:
cpp复制struct Book {
string title;
string author;
float price;
int stock;
};
Book book1 = {"C++ Primer", "Lippman", 99.9f, 50};
Book book2 = {"Effective C++", "Meyers", 69.9f, 30};
这类题目常要求对结构体数组进行排序、查找等操作。例如:
"定义一个学生结构体,包含姓名和成绩。编写函数对学生数组按成绩降序排序。"
解答:
cpp复制struct Student {
string name;
float score;
};
void sortStudents(Student arr[], int size) {
for (int i = 0; i < size-1; i++) {
for (int j = 0; j < size-i-1; j++) {
if (arr[j].score < arr[j+1].score) {
swap(arr[j], arr[j+1]);
}
}
}
}
这类题目结合函数使用,考察结构体的传递和修改。例如:
"编写函数计算学生数组的平均成绩,并找出成绩最高的学生。"
解答:
cpp复制struct Student {
string name;
float score;
};
pair<float, Student> analyzeStudents(Student arr[], int size) {
float sum = 0;
Student top = arr[0];
for (int i = 0; i < size; i++) {
sum += arr[i].score;
if (arr[i].score > top.score) {
top = arr[i];
}
}
return {sum/size, top};
}
cpp复制Student stu; // 错误:Student未定义
struct Student { ... };
修正:确保结构体声明在使用之前。
cpp复制Student* p = new Student;
p.name = "张三"; // 错误:应该用->
cpp复制struct Point { int x; int y; };
Point p = {10}; // y未初始化
cpp复制Student stu;
cout << stu.age; // 未初始化,值不确定
cpp复制Student arr[5];
arr[5].name = "张三"; // 越界访问
使用调试器查看结构体内容:
打印结构体内容:
cpp复制void printStudent(const Student& s) {
cout << "姓名:" << s.name << endl;
cout << "年龄:" << s.age << endl;
// 其他成员...
}
cpp复制#include <cassert>
Student* getStudent(int id) {
Student* p = /* 获取学生指针的逻辑 */;
assert(p != nullptr && "学生指针不能为空");
return p;
}
结构体非常适合用来表示学生信息和成绩:
cpp复制struct Score {
float math;
float english;
float programming;
};
struct Student {
int id;
string name;
Score scores;
float average() const {
return (scores.math + scores.english + scores.programming) / 3;
}
};
// 使用示例
Student stu = {1001, "张三", {90, 85, 95}};
cout << "平均分:" << stu.average() << endl;
这个例子展示了:
在图形处理中,结构体常用于表示几何图形:
cpp复制struct Point {
int x;
int y;
};
struct Rectangle {
Point topLeft;
Point bottomRight;
int width() const {
return bottomRight.x - topLeft.x;
}
int height() const {
return bottomRight.y - topLeft.y;
}
};
// 使用示例
Rectangle rect = {{10, 20}, {50, 60}};
cout << "宽度:" << rect.width() << endl;
在游戏开发中,结构体可以清晰地表示角色属性:
cpp复制struct Vector2D {
float x;
float y;
};
struct Character {
string name;
int health;
int mana;
Vector2D position;
Vector2D velocity;
void move() {
position.x += velocity.x;
position.y += velocity.y;
}
};
// 使用示例
Character player = {"英雄", 100, 50, {0, 0}, {1, 0.5}};
player.move();
这个例子展示了:
我们可以为结构体重载运算符,使其使用更自然:
cpp复制struct Point {
int x;
int y;
Point operator+(const Point& other) const {
return {x + other.x, y + other.y};
}
};
// 使用示例
Point p1 = {10, 20};
Point p2 = {5, 5};
Point p3 = p1 + p2; // {15, 25}
结构体可以与模板结合,创建通用的数据结构:
cpp复制template <typename T>
struct Pair {
T first;
T second;
void swap() {
T temp = first;
first = second;
second = temp;
}
};
// 使用示例
Pair<int> intPair = {10, 20};
intPair.swap();
Pair<string> strPair = {"Hello", "World"};
C++11及以后版本为结构体增加了许多新特性:
cpp复制struct Config {
int width = 800;
int height = 600;
string title = "My App";
};
cpp复制struct Rectangle {
int x, y, w, h;
Rectangle() : Rectangle(0, 0, 100, 100) {}
Rectangle(int x, int y, int w, int h) : x(x), y(y), w(w), h(h) {}
};
cpp复制Point p = {10, 20};
auto [x, y] = p; // x=10, y=20
在C++中,结构体(struct)和类(class)非常相似,主要区别在于默认访问权限:
除此之外,它们几乎可以互换使用。通常的编程惯例是:
例如:
cpp复制// 作为数据聚合使用
struct Point {
int x;
int y;
};
// 作为完整类使用
class Circle {
private:
Point center;
float radius;
public:
float area() const {
return 3.14159f * radius * radius;
}
// 其他方法...
};
在实际编程中,选择使用struct还是class更多是代码风格和语义表达的问题。对于GESP考试来说,理解它们的相似性和细微差别很重要。