上一课标准答案:
- 最后输出:
5因为change(x)只是把x的值复制给参数a,函数里改的是副本。
- 依次输出:
13 3原因是函数内部:
number += 10;把副本number从 3 改成 13,所以先输出 13。
但外面的:
x没有改变,仍然是 3。
- 因为:
void change(int a)采用的是值传递。a是x的副本,不是x本身。
- 一句话:
值传递就是把外部变量的值复制一份给函数参数,函数修改副本不会影响原变量。
第 14 课:引用&
这一课非常重要。
前面我们遇到的问题是:
void change(int a) { a = 100; } int main() { int x = 5; change(x); std::cout << x << std::endl; }最终:
x = 5因为:
x → 复制一份 → a但现在我就是想让函数:
直接修改原来的
x
怎么办?
C++ 可以使用:
&这就是:
引用 reference
1. 先看效果
把:
void change(int a)改成:
void change(int& a)完整代码:
#include <iostream> void change(int& a) { a = 100; } int main() { int x = 5; change(x); std::cout << x << std::endl; return 0; }这次输出:
100为什么只增加了一个:
&结果就完全不一样?
2. 值传递:给你一张复印件
以前:
void change(int a)可以想象成:
main: x ┌─────┐ │ 5 │ └─────┘ │ │ 复制 ▼ change: a ┌─────┐ │ 5 │ └─────┘所以:
a = 100;只改变下面那个盒子:
x = 5 a = 1003. 引用:不是复印,而是给原变量起“别名”
现在:
void change(int& a)可以把a理解成:
x的另一个名字。
调用:
change(x);之后可以粗略想象成:
┌─────────┐ x ─────→│ 5 │ └─────────┘ ↑ │ a这里:
x和:
a不是两个盒子。
而是:
两个名字,指向同一个变量。
所以:
a = 100;其实就是在改:
x = 100;最终外面的x也变了。
4. 最重要的对比
值传递
void change(int a)含义:
给函数一个副本修改:
a不会影响原变量。
引用传递
void change(int& a)含义:
让参数 a 直接代表原来的变量修改:
a原变量也会变化。
5.&到底放在哪里?
看:
int& a现在先把它整体理解成:
a是一个int类型的引用。
不是:
int a而是:
int& a多了:
&这告诉 C++:
不要创建新的整数副本,
a要引用外面的那个整数。
6. 一个简单例子
void addOne(int& number) { number++; }然后:
int main() { int count = 10; addOne(count); std::cout << count << std::endl; }执行:
count = 10调用:
addOne(count);此时:
number就是count的引用。
然后:
number++;实际上修改:
count:10 → 11所以最后输出:
117. 机器人例子:真正修改速度
上一课我们写:
void stopMotor(double speed) { speed = 0.0; }这个不能改变外面的速度。
现在改成:
void stopMotor(double& speed) { speed = 0.0; }然后:
int main() { double motor_speed = 100.0; stopMotor(motor_speed); std::cout << motor_speed << std::endl; }最后:
motor_speed = 0这次是真的改到了原变量。
8. 用“遥控器”来理解引用
值传递像:
拍了一张机械臂当前状态的照片给函数。
函数在照片上画来画去,不影响真实机械臂。
引用则像:
把真实机械臂的控制权交给函数。
函数一修改:
speed = 0;真实外部变量也改变。
这个区别你以后一定要记住。
9. 引用本身也可以在普通代码里创建
不只是函数参数可以用引用。
例如:
int x = 5; int& y = x;这里:
y是:
x的引用。
可以理解成:
x ─────┐ ▼ ┌──────┐ │ 5 │ └──────┘ ▲ y ─────┘所以:
y = 20;以后:
x = 20 y = 20因为它们实际上对应同一个东西。
10. 看完整程序
#include <iostream> int main() { int x = 5; int& y = x; y = 20; std::cout << x << std::endl; std::cout << y << std::endl; return 0; }输出:
20 2011. 引用不是“复制值”
这一点特别关键。
int x = 5; int y = x;和:
int x = 5; int& y = x;完全不同。
第一种:
int y = x;得到两个变量:
x = 5 y = 5之后:
y = 100;结果:
x = 5 y = 100互不影响。
第二种:
int& y = x;y是x的引用。
执行:
y = 100;结果:
x = 100 y = 10012. 引用创建时通常必须绑定变量
例如:
int x = 5; int& y = x;这里创建y的同时就告诉它:
你引用
x。
不能像普通变量那样随便理解成:
int& y;然后以后再说引用谁。
对于普通引用,通常创建时就必须绑定目标。
现在先记住这一点即可。
13. 引用一旦绑定,通常不能“换人”
看:
int x = 5; int z = 10; int& ref = x;这里:
ref绑定的是:
x如果后面写:
ref = z;初学者可能以为:
ref 改成引用 z。
不是。
实际上是:
把
z的值赋给ref所引用的x。
因为:
z = 10所以:
x 变成 10引用本身仍然是x的别名。
这个以后再深入。
14. 为什么 C++ 需要引用?
最直接的原因有两个。
第一:需要修改外部变量
例如:
void reset(double& angle) { angle = 0.0; }第二:以后处理很大的对象时,避免复制
比如以后你有:
std::vector<double>里面可能有大量数据。
如果每次传函数都完整复制一份,会浪费时间和内存。
所以以后经常会看到:
void process(std::vector<double>& data)甚至:
void process(const std::vector<double>& data)这里又出现了:
const以后我们会专门学。
现在只需要知道:
引用不仅能修改外部变量,还能避免不必要的数据复制。
15. 真实机械臂代码会经常见到
以后可能看到:
void updateJointAngle(double& angle)或者:
void calculateIK(double x, double y, double z, double& joint1, double& joint2, double& joint3)这里前三个:
x y z可能只是输入。
而:
joint1 joint2 joint3使用引用,是因为函数需要把计算结果直接写出去。
例如:
joint1 = 30.0; joint2 = 45.0; joint3 = -20.0;那么外部对应的关节变量也会改变。
16. 一个简单的 IK 风格例子
先不管真实运动学,只模拟:
void calculateAngles(double& joint1, double& joint2) { joint1 = 30.0; joint2 = 45.0; }然后:
int main() { double j1 = 0.0; double j2 = 0.0; calculateAngles(j1, j2); std::cout << j1 << std::endl; std::cout << j2 << std::endl; }输出:
30 45因为:
joint1 引用 j1 joint2 引用 j2函数直接修改了:
j1 j217. 参数是值还是引用,只差一个&
非常值得对比。
值传递
void change(int a)复制引用传递
void change(int& a)直接操作原变量所以你以后看到函数参数:
double angle和:
double& angle一定要意识到:
行为可能完全不同。
18. 一个非常重要的安全意识
引用很方便,但也意味着函数可以修改你的原变量。
例如:
void test(int& x) { x = -999; }外面:
int motor_speed = 100; test(motor_speed);结果:
motor_speed = -999所以以后看到:
&要多一个意识:
这个函数有没有可能修改我传进去的变量?
这对阅读真实工程代码非常重要。
19. 今天先不要把&和“地址”混在一起
你可能以后会听说:
& 可以取地址确实如此。
但是同一个符号:
&在不同位置可能有不同含义。
今天我们只学习:
int& a这里的&表示:
引用
“取地址”我们学习指针时再讲,否则现在很容易混乱。
20. 本课核心图
值传递:
x ┌───┐ │ 5 │ └───┘ │ │复制 ▼ a ┌───┐ │ 5 │ └───┘引用:
x ───┐ ▼ ┌─────┐ │ 5 │ └─────┘ ▲ a ───┘这个图如果真正理解了,引用就掌握一半了。
本课小练习
第 1 题
void change(int& a) { a = 20; } int main() { int x = 5; change(x); std::cout << x << std::endl; }最终输出什么?
第 2 题
int x = 10; int& y = x; y = 30;最后:
x = ? y = ?第 3 题
下面两种写法有什么本质区别?
void test(int a)和:
void test(int& a)第 4 题
void setZero(double& angle) { angle = 0.0; }如果:
double joint1 = 45.0; setZero(joint1);最后joint1是多少?