第6.3章: 传递实参

English

概述

当你调用函数时,你把实参传给它的形参。 C++ 提供了多种传递实参的方式,各有不同的行为与 性能特征。

在本章中,你将学到:

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);
}

这对大型对象非常理想,例如:

它能避免复制,同时保证函数无法改动数据。

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. 何时使用哪种方式

在以下情况使用按值传递:

在以下情况使用按引用传递:

在以下情况使用按 const 引用传递:

这三种形式覆盖了现代 C++ 中几乎所有的函数形参用法。

要点