函数参数 (function parameter)#

按传值、传指针、传引用来区分参数是没有意义的, 我们更应该考虑参数的实际用途: 不是去考虑我们应该怎样传参, 而是 去考虑我们要用这个参数做什么.

对于一个变量我们可以进行读取和写入, 那么对于参数也是如此:

输入参数 (in parameter): 通过参数为函数输入值.

函数只从实际参数中读取信息, 而不修改实际参数的信息.

 1void function(int value) {
 2  value = value * 5;
 3  cout << value;  // 输出 5
 4}
 5
 6int main() {
 7  int value = 1;
 8  function(value);
 9  cout << value;  // 输出 1
10}
输出参数 (out parameter): 通过参数为函数输出值.

函数只修改实际参数的信息, 而不读取实际参数的任何信息.

1void function(int& value) {
2  value = 5;
3}
4
5int main() {
6  int value = 1;
7  function(value);
8  cout << value;  // 输出 5
9}
输入输出参数 (in-out parameter): 通过参数为函数输入值, 并输出值.

函数既从实际参数中读取信息, 又修改实际参数的信息.

 1void function(int& value) {
 2  std::cout << value;  // 输出 1
 3  value = 5;
 4}
 5
 6int main() {
 7  int value = 1;
 8  function(value);
 9  cout << value;  // 输出 5
10}

传参决策#

../../../_images/function_parameter.png

C++98 传参决策#

决策由来: 常见传参方式的用途分析#

我们来分析一下见过的传参方式, 对它们的用途进行分类.

其中 parameter 表示参数列表中的形式参数, argument 表示调用时传递的实际参数.

function(int parameter)

按值传参发生拷贝, int parameter = argument 是从实际参数拷贝得到的新对象, 因此自然不可能通过 parameter 修改 argument, 是 输入参数.

function(int& parameter)

按引用传参, int& parameter = argument 是对实际参数的引用, 对 parameter 的任何操作其实就是对 argument 的操作, 是 输出参数 (如果只写入) 或 输入输出参数 (既读取又写入).

function(int const& parameter)

按 const 引用传参, int const& parameter = argument 是对实际参数的引用, 对 parameter 的任何操作其实就是对 argument 的操作, 但这个引用只能用于读取不能用于写入, 是 输入参数.

function(int* parameter)

按指针传参, int* parameter = &argument 指向实际参数, 对 *parameter 的任何操作其实就是对 argument 的操作, 是 输出参数 (如果只写入) 或 输入输出参数 (既读取又写入).

function(int const* parameter)

按 const 指针传参, int const* parameter = &argument 指向实际参数, 对 *parameter 的任何操作其实就是对 argument 的操作, 但这个解引用只能用于读取不能用于写入, 是 输入参数.

决策由来: 使用返回值而非输出参数#

输出参数方式并不直接:

 1void function(int& value) {
 2  value = 5;
 3}
 4
 5/* 中间隔了几百行代码 */
 6
 7int main() {
 8  int value = 1;
 9  function(value);     // 传入参数, 应该不会改变我的 value, 对、对吧?
10  std::cout << value;  // 输出 5😱
11}

相比于输出参数, 我们有一个更好的输出方案——返回值:

1int function() {
2  return 5;
3}
4
5int main() {
6  int value = function();
7}

如果需要返回多个值, 使用结构体:

1struct Symmetric_minus_result {
2  int lhs_minus_rhs;
3  int rhs_minus_lhs;
4};
5
6Symmetric_minus_result symmetric_minus(int lhs, int rhs) {
7  return {lhs - rhs, rhs - lhs};
8}

传值? 传指针?#

这样以参数的实际用途来分析, 才是最反映我们编写代码的逻辑的, 也是最符合事实的: 传指针其实也是传值, 之所以区别开来教, 就是内含了用参数的实际用途来区别的想法.

by_value(int value)by_pointer(int* pointer) 为例.

by_value(int value) 调用时, 拷贝的是 int 类型; 而 by_pointer(int* pointer) 调用时, 拷贝的是 int* 类型.

指针 (pointer) 中已指出, 指针是一个对象, 它存储了指向对象的地址, 自然拷贝得到的新指针也存储了那个地址. 因此自然地, 这个新指针解引用也能访问那个指向的对象:

 1int temp = 5;
 2
 3void function(int* pointer) {
 4  pointer = &temp;  // pointer 指向 temp
 5}
 6
 7int main() {
 8  int value    = 0;
 9  int* pointer = &value;  // &value 取地址, 得到指向 value 的一个指针
10
11  function(pointer);      // 调用时这个指针也是传值, 发生拷贝, 只是拷贝得到的指针也指向 value
12
13  std::cout << *pointer;  // 输出 0, 它依然指向 value!
14}

相关解答#