07、语义和引用语义
同样是在函数里把参数改成 2,有时调用者的变量不变,有时却跟着改变。区别不在赋值语句,而在于:函数操作的是一个新对象,还是原对象的另一个名字。
值语义与引用语义先看可观察结果
本文使用 C++17。对于 int,按值传参会建立独立形参;按非 const 左值引用传参,形参绑定到调用者对象。修改前者只影响副本,修改后者会作用于原对象。
“值语义”描述的是独立值的行为,不代表所有类型都只做几个字节的浅拷贝;一个 vector 的复制通常需要复制元素,一个裸指针的复制却只复制地址,所指对象仍可能共享。
按值传递:先复制,再改副本
设调用者有 value = 100,函数接收 int v。调用时 v 得到 100,之后将 v 改成 2,调用者的 value 仍为 100。

这里有两个独立 int 对象。编译器可以优化具体指令,但不能因此改变程序允许观察到的结果。
引用传递:两个名字,一个对象
把参数改成 int& v,v 绑定到原来的 value。通过 v 写入 2,就是在修改同一个 int 对象。

引用不是“标准规定必定额外分配的指针槽”。可以用别名理解它;具体实现怎样传递地址属于 ABI 与优化细节。引用初始化后不能重新绑定,写 v = other 是赋值给所绑定对象,而不是让 v 改绑。
C++17 可运行示例
#include <cassert>
#include <iostream>
void setByValue(int value) {
value = 2;
std::cout << value << '\n';
}
void setByReference(int& value) {
value = 2;
}
int main() {
int value = 100;
setByValue(value);
assert(value == 100);
std::cout << value << '\n';
setByReference(value);
assert(value == 2);
std::cout << value << '\n';
}
输出依次为 2、100、2。函数里的第一行输出展示副本发生变化,而不是调用者已经被修改。
怎样选择参数形式
小型标量按值通常直观;要修改调用者对象时使用合适的非 const 引用或指针;较大的只读对象常用 const T& 避免不必要复制。指针还能表达可空、重定向等意图,但也增加生命周期责任。
不要机械地认为所有参数都该改成引用。const T& 虽限制经该引用写入,却不自动拥有对象,也不保证任何时候都没有其他代码修改对象;引用悬空同样是错误。
常见误区
- 通过一个按值传入的 int* 修改 *p,会改变所指对象,但仍然是“指针按值传递”,不是 C++ 引用传递。
- 绑定一个 T& 或 T&& 本身,不会自动复制或移动资源。
- 返回局部变量的引用,在局部对象销毁后就不再有效;不能用“少一次复制”合理化悬空引用。
- 函数参数按值不等于一定深拷贝,具体复制行为由类型定义。
练习与依据
将参数分别改为 int、int&、const int&,解释函数能否赋值以及调用者是否变化;再用 vector 与裸指针比较“复制对象”和“复制地址”的差别。
可核对 C++17 引用初始化规则,不要把教学别名模型误当作所有实现的物理布局。
