答案与下一课
上一课标准答案:
'A'
是 char,表示单个字符。
"A"
是字符串,通常配合 std::string 使用。
name[0] = 'R' name[2] = 'b' name.size() = 5
因为:
R o b o t 0 1 2 3 4
- 输出:
1
因为:
state == "ERROR"
成立。
- 最后:
c = "Joint Ready"
因为 std::string 可以使用 + 进行字符串拼接。
第 21 课:struct ------ 把一组相关数据打包在一起
现在开始进入一个非常重要的新阶段。
前面我们保存机械臂关节信息,可能会写:
double angle = 30.0;
double speed = 10.0;
double temperature = 45.0;
bool enabled = true;
这些变量都在描述:
同一个关节。
问题来了:
如果有 6 个关节怎么办?
可能变成:
double joint1_angle;
double joint1_speed;
double joint1_temperature;
bool joint1_enabled;
double joint2_angle;
double joint2_speed;
double joint2_temperature;
bool joint2_enabled;
// ...
很快就会乱。
这时候我们需要:
struct
也就是:
结构体
1. struct 最直觉的理解
你可以把 struct 理解成:
自己设计一种新的"数据盒子"。
以前:
int
double
bool
都是 C++ 已经提供的数据类型。
比如:
double angle = 30.0;
但现在我们想定义一种新的类型:
Joint
这个类型里面同时拥有:
角度 速度 温度 是否使能
于是:
struct Joint
{
double angle;
double speed;
double temperature;
bool enabled;
};
这就是我们自己定义的:
Joint 类型。
2. 先不要把 struct 想得太复杂
以前:
double angle;
一个变量只能直接代表一个数据。
而现在:
Joint joint1;
这个变量里面可以装很多相关数据:
joint1 ┌────────────────────┐ │ angle │ │ speed │ │ temperature │ │ enabled │ └────────────────────┘
所以:
struct = 把多个相关变量打包到一起。
3. 逐行解释
看:
struct Joint
{
double angle;
double speed;
bool enabled;
};
第一行:
struct Joint
意思是:
我要定义一种新的结构体类型,名字叫 Joint。
然后:
{
表示这个结构体内部有哪些成员。
double angle;
表示:
每一个 Joint 都有一个 double 类型的 angle。
double speed;
表示:
每个 Joint 都有一个 speed。
bool enabled;
表示:
每个 Joint 都有一个真假状态 enabled。
最后:
};
特别注意:
struct 定义结束的 } 后面还有一个分号 ;
也就是:
};
这和普通函数:
void test()
{
}
不一样。
4. 定义完 Joint 后怎么用?
定义:
struct Joint
{
double angle;
double speed;
bool enabled;
};
然后可以:
Joint joint1;
这和:
int a;
非常像。
只不过:
int
是 C++ 自带类型。
而:
Joint
是我们刚才自己定义的类型。
5. 怎么访问里面的数据?
需要使用:
.
这个点号。
例如:
joint1.angle = 30.0;
joint1.speed = 10.0;
joint1.enabled = true;
你可以读成:
joint1 的 angle joint1 的 speed joint1 的 enabled
所以:
.
你现在可以先理解成:
"里面的"
6. 和之前的 std::vector 联系起来
你之前看过:
data.push_back(10);
这里也有:
.
其实:
data.push_back(...)
也可以粗略读成:
data 里面的 push_back() 功能。
而:
joint1.angle
就是:
joint1 里面的 angle。
后面学 class 时,我们会正式解释这个点号。
7. 一个完整例子
#include <iostream>
struct Joint
{
double angle;
double speed;
bool enabled;
};
int main()
{
Joint joint1;
joint1.angle = 30.0;
joint1.speed = 5.0;
joint1.enabled = true;
std::cout << joint1.angle << std::endl;
std::cout << joint1.speed << std::endl;
return 0;
}
输出:
30 5
8. 内存思维
可以把:
Joint joint1;
想成一个大盒子:
joint1 ┌─────────────────┐ │ angle │ → 30.0 ├─────────────────┤ │ speed │ → 5.0 ├─────────────────┤ │ enabled │ → true └─────────────────┘
所以 joint1 不是单纯保存一个数。
它是一整组数据。
9. 为什么这对机器人特别重要?
因为现实中的"关节"本来就是一个整体对象。
一个机械臂关节可能有:
关节角度 目标角度 速度 电流 温度 故障状态 是否使能
这些数据本来就属于同一个关节。
所以可以写:
struct Joint
{
double current_angle;
double target_angle;
double speed;
double current;
double temperature;
bool enabled;
bool error;
};
这样比定义一堆散乱变量清晰得多。
10. 创建多个关节
定义一次:
struct Joint
{
double angle;
double speed;
};
以后可以创建:
Joint joint1;
Joint joint2;
Joint joint3;
每个都是独立的数据。
例如:
joint1.angle = 10.0;
joint2.angle = 20.0;
joint3.angle = 30.0;
它们互不影响。
11. 就像设计了一个"模板"
你可以把:
struct Joint
{
double angle;
double speed;
};
理解成:
Joint 的设计图纸。
而:
Joint joint1;
Joint joint2;
就是按照这个设计图纸造了两个实际对象。
类似:
Joint 图纸 ┌─────────────┐ │ angle │ │ speed │ └─────────────┘ 制造: joint1 ┌─────────────┐ │ angle = 10 │ │ speed = 5 │ └─────────────┘ joint2 ┌─────────────┐ │ angle = 20 │ │ speed = 8 │ └─────────────┘
12. 创建时直接赋值
也可以写:
Joint joint1 = {30.0, 5.0, true};
如果结构体定义是:
struct Joint
{
double angle;
double speed;
bool enabled;
};
那么按照顺序:
30.0 → angle 5.0 → speed true → enabled
所以:
Joint joint1 = {30.0, 5.0, true};
就相当于:
joint1.angle = 30.0;
joint1.speed = 5.0;
joint1.enabled = true;
13. 顺序一定要注意
假设:
struct Joint
{
double angle;
double speed;
bool enabled;
};
那么:
Joint joint1 = {30.0, 5.0, true};
是按结构体成员定义顺序对应的。
所以如果成员很多,这种写法可能不如逐个赋值直观。
初学阶段建议你优先看懂:
joint1.angle = 30.0;
joint1.speed = 5.0;
14. struct 里面可以有 std::string
例如:
#include <string>
struct Joint
{
std::string name;
double angle;
bool enabled;
};
然后:
Joint joint1;
joint1.name = "Joint1";
joint1.angle = 30.0;
joint1.enabled = true;
于是一个关节同时拥有:
名字 角度 状态
15. 完整机器人例子
#include <iostream>
#include <string>
struct Joint
{
std::string name;
double angle;
double speed;
bool enabled;
};
int main()
{
Joint joint1;
joint1.name = "Joint1";
joint1.angle = 30.0;
joint1.speed = 5.0;
joint1.enabled = true;
std::cout << joint1.name << std::endl;
std::cout << joint1.angle << std::endl;
std::cout << joint1.speed << std::endl;
return 0;
}
输出:
Joint1 30 5
16. struct + 数组 非常适合六轴机械臂
现在开始真正结合起来。
我们已经定义:
struct Joint
{
double angle;
double speed;
bool enabled;
};
然后可以创建:
Joint joints[6];
这是什么意思?
不是:
6 个 double
而是:
6 个 Joint
可以画成:
joints [0] [1] [2] ... ┌─────────┐ ┌─────────┐ ┌─────────┐ │ angle │ │ angle │ │ angle │ │ speed │ │ speed │ │ speed │ │ enabled │ │ enabled │ │ enabled │ └─────────┘ └─────────┘ └─────────┘
这已经很像真实六自由度机械臂的数据结构了。
17. 怎么访问?
例如:
joints[0].angle = 10.0;
拆开:
joints[0]
表示:
第 1 个 Joint。
然后:
.angle
表示:
这个 Joint 里面的 angle。
所以:
joints[0].angle
就是:
第 1 个关节的角度。
18. 六轴机械臂例子
Joint joints[6];
joints[0].angle = 10.0;
joints[1].angle = 20.0;
joints[2].angle = 30.0;
joints[3].angle = 40.0;
joints[4].angle = 50.0;
joints[5].angle = 60.0;
然后可以配合:
for
统一输出:
for (int i = 0; i < 6; i++)
{
std::cout << joints[i].angle << std::endl;
}
输出:
10 20 30 40 50 60
19. 现在你已经把很多知识串起来了
看这一句:
std::cout << joints[i].angle << std::endl;
你现在可以一层层拆:
joints ↓ 一个 Joint 数组 [i] ↓ 取第 i 个 Joint .angle ↓ 取这个 Joint 里面的 angle std::cout ↓ 输出它
这就是阅读复杂 C++ 代码的正确方法:
一层一层拆,而不是整句硬看。
20. struct 也能传进函数
例如:
void printJoint(Joint joint)
{
std::cout << joint.angle << std::endl;
}
然后:
Joint j;
j.angle = 30.0;
printJoint(j);
输出:
30
不过这里:
Joint joint
仍然是:
值传递。
也就是说,会复制一个 Joint。
21. 又和引用连起来了
如果结构体比较大,我们可能不希望复制。
就可能写:
void printJoint(const Joint& joint)
{
std::cout << joint.angle << std::endl;
}
这里出现了:
const Joint& joint
你已经认识:
&
表示引用。
但:
const
我们还没正式学。
所以现在不要硬啃这一句。
你只需要知道:
以后大型结构体经常通过引用传入函数,避免复制。
下一阶段会讲 const。
22. struct 和 class 有什么关系?
你目标里还有:
class 面向对象
其实:
struct
和:
class
关系非常近。
现在我们先把 struct 理解成:
打包数据。
以后 class 会进一步变成:
数据 + 操作这些数据的函数
例如:
class Robot
{
// 机器人数据
// 机器人功能
};
所以 struct 是学习类非常好的过渡。
23. 本课核心
以前:
double angle;
double speed;
bool enabled;
是三个散乱变量。
现在:
struct Joint
{
double angle;
double speed;
bool enabled;
};
把它们打包成一种新类型:
Joint
然后:
Joint joint1;
创建一个具体关节。
使用:
joint1.angle
joint1.speed
joint1.enabled
本课小练习
第 1 题
struct Motor
{
double speed;
bool enabled;
};
那么:
Motor m;
m.speed = 100.0;
m.enabled = true;
m.speed 是多少?
第 2 题
下面:
struct Joint
{
double angle;
double speed;
};
Joint 是变量名还是我们自己定义的新类型?
第 3 题
Joint joints[6];
是什么意思?
第 4 题
joints[2].angle = 45.0;
用自己的话解释这句话。
下一课我们讲:
第 22 课:const ------ "这个值不允许被修改"
你会开始理解:
const double PI = 3.14159;
以及为什么真实工程里经常写:
void printJoint(const Joint& joint)
这会把你之前学过的变量、引用、函数参数、struct真正连起来。