文章目录
1. 定义结构体
在C++中,结构体(struct
)是一种用户定义的数据类型,它允许将不同类型的数据组合在一起形成一个复合数据类型。结构体在组织和管理相关数据方面非常有用,例如在表示复杂的实体(如学生、汽车、图形对象等)时,可以将相关的属性(如名称、年龄、颜色、位置等)组合在一起。通过使用结构体,程序员可以更清晰地表达和操作这些复杂的数据结构。
结构体的基本定义
在C++中,定义一个结构体通常使用struct
关键字。结构体的定义包含结构体的名称和内部的成员变量。以下是一个简单的结构体定义示例:
#include <iostream>
using namespace std;
// 定义一个表示点(Point)的结构体
struct Point {
int x; // x坐标
int y; // y坐标
};
int main() {
// 创建一个 Point 结构体变量
Point p1;
p1.x = 10; // 访问并设置 x 成员
p1.y = 20; // 访问并设置 y 成员
cout << "Point p1: (" << p1.x << ", " << p1.y << ")" << endl;
return 0;
}
在这个例子中定义了一个名为Point
的结构体,它包含两个成员变量x
和y
,分别表示一个点的横坐标和纵坐标。在main
函数中,我们创建了一个Point
类型的变量p1
,并设置了x
和y
的值,最后输出这个点的坐标。
结构体定义的语法细节
-
struct
关键字:在C++中,使用struct
关键字来定义结构体。它与class
关键字类似,但有一些区别,特别是在默认访问控制方面(struct
默认是public,而class
默认是private)。 -
结构体名称:结构体名称紧随
struct
关键字之后,它是新数据类型的名称。在上例中,Point
就是结构体的名称。 -
成员变量:结构体的成员变量定义在大括号
{}
内,可以是任意基本数据类型(如int
、float
、char
)或其他结构体类型,甚至可以是指针、数组或对象。在结构体定义后需要使用分号;
结束。 -
访问结构体成员:一旦定义了结构体类型,就可以使用点操作符(
.
)来访问结构体变量的成员。通过结构体变量的名字和成员变量的名字,可以读取或修改成员的值。
结构体的初始化
直接赋值初始化:结构体变量可以在定义时通过大括号{}
直接初始化:
Point p2 = {30, 40}; // 初始化 Point 变量 p2,x=30, y=40
逐成员赋值:也可以像之前的例子那样,通过逐个成员的方式进行赋值:
Point p3;
p3.x = 50;
p3.y = 60;
构造函数初始化:如果在结构体中定义了构造函数,则可以通过构造函数来初始化结构体变量:
struct Point {
int x;
int y;
// 构造函数
Point(int xVal, int yVal) : x(xVal), y(yVal) {}
};
Point p4(70, 80); // 通过构造函数初始化 p4,x=70, y=80
嵌套结构体
结构体可以嵌套定义,这意味着一个结构体的成员可以是另一个结构体类型。这在表示复杂的数据结构时非常有用。
#include <iostream>
using namespace std;
struct Circle {
Point center; // 圆心
double radius; // 半径
};
int main() {
Circle c1;
c1.center.x = 5; // 设置圆心的 x 坐标
c1.center.y = 10; // 设置圆心的 y 坐标
c1.radius = 15.5; // 设置半径
cout << "Circle c1: Center(" << c1.center.x << ", " << c1.center.y << "), Radius: " << c1.radius << endl;
return 0;
}
在这个例子中定义了一个Circle
结构体,包含一个Point
类型的成员center
(表示圆心),以及一个double
类型的成员radius
(表示半径)。
2. 访问结构成员
定义了结构体后,可以通过结构体变量访问其成员。访问结构体成员是指在使用结构体变量时,读取或修改结构体中的各个成员变量。
通过点操作符访问成员
最常见的访问结构体成员的方法是使用点操作符(.
)。点操作符用于连接结构体变量和成员变量,以便访问或修改该成员的值。
#include <iostream>
using namespace std;
struct Point {
int x;
int y;
};
int main() {
Point p1; // 定义一个 Point 结构体变量 p1
// 使用点操作符访问和修改成员变量
p1.x = 10;
p1.y = 20;
cout << "Point p1: (" << p1.x << ", " << p1.y << ")" << endl;
return 0;
}
访问嵌套结构体的成员
如果一个结构体包含另一个结构体作为其成员,可以使用点操作符链式访问嵌套结构体的成员。这种方式非常直观,可以很好地表示嵌套的数据关系。
#include <iostream>
using namespace std;
struct Point {
int x;
int y;
};
struct Rectangle {
Point topLeft; // 矩形的左上角坐标
Point bottomRight; // 矩形的右下角坐标
};
int main() {
Rectangle rect; // 定义一个 Rectangle 结构体变量 rect
// 访问并设置嵌套的 Point 结构体成员
rect.topLeft.x = 0;
rect.topLeft.y = 10;
rect.bottomRight.x = 20;
rect.bottomRight.y = 0;
cout << "Rectangle corners: (" << rect.topLeft.x << ", " << rect.topLeft.y << ") to ("
<< rect.bottomRight.x << ", " << rect.bottomRight.y << ")" << endl;
return 0;
}
在这个示例中,Rectangle
结构体包含两个Point
结构体作为其成员。我们通过点操作符链式访问这些嵌套的成员,设置和获取矩形的左上角和右下角的坐标。
通过指针访问结构体成员
在某些情况下,你可能会通过指针来操作结构体。C++允许通过指针访问结构体的成员,并提供了两种语法:**间接访问符(->
)**和点操作符配合解引用操作符(*
)。
-
间接访问符
->
:当你有一个指向结构体的指针时,可以使用->
符号直接访问结构体成员。
Point* pPtr = &p1; // 定义一个指向结构体的指针
pPtr->x = 15; // 使用间接访问符访问成员
pPtr->y = 25;
cout << "Point p1: (" << pPtr->x << ", " << pPtr->y << ")" << endl;
- 点操作符与解引用操作符组合:也可以先解引用指针,然后使用点操作符访问成员。
(*pPtr).x = 30; // 先解引用指针,再使用点操作符访问成员
(*pPtr).y = 40;
cout << "Point p1: (" << (*pPtr).x << ", " << (*pPtr).y << ")" << endl;
在实际编程中,使用->
符号更为常见,因为它语法更加简洁且易读。
3. 结构作为函数参数
结构体不仅可以用于定义复杂的数据类型,还可以作为函数参数进行传递。在函数中,结构体可以通过值传递、引用传递或指针传递来处理。
通过值传递结构体
值传递是指将结构体的副本传递给函数。这意味着在函数内部对结构体参数所做的任何修改都不会影响原来的结构体。值传递的优点是简单直观,但如果结构体很大,传递的副本可能会占用较多的内存,并且由于复制的开销,效率较低。
#include <iostream>
using namespace std;
struct Point {
int x;
int y;
};
void printPoint(Point p) { // 通过值传递结构体
p.x += 10; // 修改不会影响原始结构体
cout << "Point inside function: (" << p.x << ", " << p.y << ")" << endl;
}
int main() {
Point p1 = {10, 20};
printPoint(p1); // 传递结构体副本
cout << "Point in main: (" << p1.x << ", " << p1.y << ")" << endl;
return 0;
}
输出结果:
Point inside function: (20, 20)
Point in main: (10, 20)
- 在这个示例中,
printPoint
函数接受一个Point
类型的参数p
,这是通过值传递的。 - 在
printPoint
函数内部,p.x
的值被增加了10,但这不会影响原始的p1
变量,因为修改的是p1
的副本。 - 当函数执行完毕返回到
main
时,p1
的值保持不变。
通过引用传递结构体
引用传递是指将结构体的引用(即结构体本身的别名)传递给函数。这意味着函数内部对结构体的修改会影响到原始的结构体。引用传递的效率更高,因为不需要复制结构体的内容,只是传递了一个引用,适合处理大型结构体。
#include <iostream>
using namespace std;
struct Point {
int x;
int y;
};
void movePoint(Point &p, int dx, int dy) { // 通过引用传递结构体
p.x += dx;
p.y += dy;
}
int main() {
Point p1 = {10, 20};
movePoint(p1, 5, -10); // 直接修改原始结构体
cout << "Point after moving: (" << p1.x << ", " << p1.y << ")" << endl;
return 0;
}
输出结果:
Point after moving: (15, 10)
- 在这个示例中,
movePoint
函数接受一个Point
类型的引用p
。函数对p
的修改将直接作用于原始的p1
变量。 - 当
movePoint
函数执行完毕后,p1
的x
和y
值已经被改变,这是因为引用传递操作的是同一个对象。
通过指针传递结构体
指针传递与引用传递类似,也是为了避免结构体的复制开销,但使用指针传递时,需要显式地解引用指针来访问结构体成员。指针传递在需要传递空指针(nullptr
)的情况下特别有用,能够表示没有传递任何有效的结构体。
#include <iostream>
using namespace std;
struct Point {
int x;
int y;
};
void scalePoint(Point *p, int factor) { // 通过指针传递结构体
p->x *= factor; // 使用箭头操作符访问结构体成员
p->y *= factor;
}
int main() {
Point p1 = {10, 20};
scalePoint(&p1, 2); // 传递结构体的指针
cout << "Point after scaling: (" << p1.x << ", " << p1.y << ")" << endl;
return 0;
}
输出结果:
Point after scaling: (20, 40)
- 在这个示例中,
scalePoint
函数接受一个Point
类型的指针p
。函数内部使用p->
语法来访问结构体的成员变量。 - 通过指针传递,函数可以直接修改原始结构体
p1
的值,操作完成后,p1
的x
和y
值被成倍增加。
4. 指向结构的指针
指向结构体的指针是一个非常有用的工具,特别是在处理动态内存分配、结构体数组以及传递结构体参数时。
定义和初始化指向结构体的指针
定义指向结构体的指针与定义其他类型的指针类似。首先,我们定义一个结构体类型,然后定义一个指向这种结构体的指针。
#include <iostream>
using namespace std;
struct Point {
int x;
int y;
};
int main() {
Point p1 = {10, 20}; // 定义一个 Point 结构体变量
Point* pPtr = &p1; // 定义一个指向 Point 结构体的指针
cout << "Address of p1: " << pPtr << endl;
cout << "Point p1: (" << pPtr->x << ", " << pPtr->y << ")" << endl;
return 0;
}
Point* pPtr = &p1;
:这里定义了一个指针pPtr
,它指向结构体p1
的地址。- 使用
pPtr
可以访问结构体p1
的成员变量,通过pPtr->x
和pPtr->y
来读取和修改结构体成员。
动态分配结构体内存
在C++中,指针非常有用的一部分是能够动态分配内存。这对于需要在运行时创建结构体实例的情况特别重要。可以使用new
操作符来动态分配结构体内存,并使用指针来管理它。
#include <iostream>
using namespace std;
struct Point {
int x;
int y;
};
int main() {
// 动态分配一个 Point 结构体的内存
Point* pPtr = new Point;
// 通过指针设置结构体成员
pPtr->x = 50;
pPtr->y = 100;
cout << "Dynamically allocated Point: (" << pPtr->x << ", " << pPtr->y << ")" << endl;
// 释放动态分配的内存
delete pPtr;
return 0;
}
输出结果:
Dynamically allocated Point: (50, 100)
5. typedef 关键字
typedef
关键字用于为已有的数据类型创建一个新的名称(别名)。这可以使代码更简洁、易读,并且在某些情况下提供了更好的可维护性。typedef
特别有用,当你需要频繁使用复杂的数据类型时,或者希望为某些类型创建一个更具描述性的名字时。
基本用法
typedef
的基本语法是:typedef
原始类型 新类型名;。使用typedef
定义的新类型名,可以像使用原始类型一样使用。
#include <iostream>
using namespace std;
typedef unsigned long int ulong; // 为 unsigned long int 定义别名
int main() {
ulong largeNumber = 1000000; // 使用 typedef 定义的新类型名
cout << "Large Number: " << largeNumber << endl;
return 0;
}
typedef unsigned long int ulong;
:这行代码将unsigned long int
重命名为ulong
,使得在代码中可以用ulong
来代替unsigned long int
,使代码更简洁。- 在
main
函数中,ulong
类型的变量largeNumber
被定义为1000000
,并输出其值。
使用 typedef
定义结构体别名
结构体的类型名通常比较长或复杂,使用typedef
可以为结构体类型定义一个简短的别名,使代码更加易读。
#include <iostream>
using namespace std;
struct Point {
int x;
int y;
};
typedef struct Point Point2D; // 定义 Point 结构体的别名为 Point2D
int main() {
Point2D p1; // 使用别名 Point2D 定义结构体变量
p1.x = 10;
p1.y = 20;
cout << "Point p1: (" << p1.x << ", " << p1.y << ")" << endl;
return 0;
}
typedef struct Point Point2D;
:将struct Point
类型重命名为Point2D
,在后续代码中可以使用Point2D
来定义结构体变量。- 在
main
函数中,使用Point2D
类型定义了一个结构体变量p1
,并设置其成员值。
注意: 在C++中,可以直接省略struct
前缀,在结构体定义时直接使用typedef
来简化结构体的定义:
typedef struct {
int x;
int y;
} Point2D;
这样在定义结构体时就不需要显式给出结构体的名字,只需要使用别名Point2D
。