第6.3章: 传递实参
概述
当你调用函数时,你把实参传给它的形参。 C++ 提供了多种传递实参的方式,各有不同的行为与 性能特征。
在本章中,你将学到:
- 按值传递
- 按引用传递(
&) - 按 const 引用传递(
T const &) - 为什么某些函数没有引用就无法工作
::std::ranges::swap与::std::ranges::iter_swap如何工作
1. 按值传递
当你按值传递实参时,函数收到的是该实参自身的 副本。
void increment(int x)
{
++x; // modifies the copy
}
int main()
{
int a{5};
increment(a);
// a is still 5
}
按值传递简单且安全,但复制大对象可能很昂贵。
2. 按引用传递(&)
当你按引用传递实参时,函数收到的是原对象的 别名。不会发生复制。
void increment(int & x)
{
++x; // modifies the original
}
int main()
{
int a{5};
increment(a);
// a is now 6
}
按引用传递允许函数修改调用者的变量。
3. 按 const 引用传递(T const &)
当你按 const 引用传递实参时,函数收到的是 原对象的引用,但不能修改它。
void print_twice(::fast_io::string const & s)
{
::fast_io::io::println(s);
::fast_io::io::println(s);
}
这对大型对象非常理想,例如:
::fast_io::string::fast_io::vector<T>- 结构体与类
它能避免复制,同时保证函数无法改动数据。
4. 为什么按值传递无法修改调用者(swap 示例)
一个经典例子是 swap 函数。如果你用
按值传递来写它,它不会工作。
void bad_swap(int a, int b)
{
int temp{a};
a = b;
b = temp;
}
int main()
{
int x{10};
int y{20};
bad_swap(x, y);
// x is still 10, y is still 20
}
这失败是因为 a 与 b 都是副本。
用引用正确实现 swap
void good_swap(int & a, int & b)
{
int temp{a};
a = b;
b = temp;
}
int main()
{
int x{10};
int y{20};
good_swap(x, y);
// x is now 20, y is now 10
}
这里 a 与 b 引用的是原始变量。
使用 ::std::ranges::swap(对象的首选)
#include <concepts> // defines ::std::ranges::swap
int main()
{
int x{10};
int y{20};
::std::ranges::swap(x, y);
// x is now 20, y is now 10
}
::std::ranges::swap 是现代、标准的对象交换方式。
使用指针与 ::std::ranges::iter_swap
当你手上有指针或迭代器时,请使用 ::std::ranges::iter_swap。
#include <iterator> // defines ::std::ranges::iter_swap
int main()
{
int x{10};
int y{20};
int * px{::std::addressof(x)};
int * py{::std::addressof(y)};
::std::ranges::iter_swap(px, py); // swaps *px and *py
// x is now 20, y is now 10
}
::std::ranges::iter_swap 交换迭代器或指针所指向的值。
这些函数在哪里定义
#include <concepts> // ::std::ranges::swap
#include <iterator> // ::std::ranges::iter_swap
这些头文件提供了支撑 ranges 版 swap 的概念与迭代器
工具。
5. 何时使用哪种方式
在以下情况使用按值传递:
- 形参很小(例如
int、double) - 你本来就需要一个局部副本
在以下情况使用按引用传递:
- 函数必须修改调用者的变量
在以下情况使用按 const 引用传递:
- 对象很大
- 你不想复制它
- 函数不应当修改它
这三种形式覆盖了现代 C++ 中几乎所有的函数形参用法。
要点
- 按值传递会复制实参。
- 按引用传递允许修改原对象。
- 按 const 引用传递避免复制,同时防止修改。
- 对字符串、vector 这类大型对象使用
T const &。 - 对
int、double这类小类型,按值传递即可。 - 用按值传递写的 swap 无法工作。
- 对象的交换使用
::std::ranges::swap。 - 指针与迭代器的交换使用
::std::ranges::iter_swap。 ::std::ranges::swap定义在<concepts>中。::std::ranges::iter_swap定义在<iterator>中。