c++函数的传参

前言

在阐述c++中函数传参之前,先说明一下为何写这篇博客。事实上,c++的传参方式的讨论已经很多,但是在解剑指 Offer 27. 二叉树的镜像(Leetcode 226翻转二叉树)时发现三种传参方式自己并没有完全弄清,在以树指针为对象时,就出现错误。

交换两个变量

网上有三种常见的交换变量的方式,第一种创建临时变量存储其中一个待交换的值,这种方法最为常见。

1
2
3
auto tmp = a;
a = b;
b = tmp;

第二种不创建临时变量,利用四则运算,代码如下,但是可能会产生数值溢出,不建议使用。

1
2
3
a = a + b;
b = a - b;
a = a - b;

第三种同样不创建临时变量,利用位运算,代码如下。这是因为 x ^ y ^ y = x,任意变量与其他变量异或两次结果等于自身。然而,该方法只适用于交换整型或者字符型数据交换,这是因为位运算的运算分量只能是这两者。

1
2
3
a = a ^ b;
b = a ^ b;
a = a ^ b;

所以,最简单的才是最有用的,第一种方法虽然朴素,但是有效。

问题描述

在翻转二叉树时,其实问题本身解答非常容易,Leetcode也是定级为简单。就是个普通的前序遍历或者后序遍历,前者自顶向下,后者自底向上。但是在交换两个TreeNode*变量时,自己写的交换函数竟不能通过。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* TreeNode *left;
* TreeNode *right;
* TreeNode(int x) : val(x), left(NULL), right(NULL) {}
* };
*/
class Solution {
public:
TreeNode* invertTree(TreeNode* root) {
if(!root) return root;
TreeNode* tmp = root->left;
root->left = root->right;
root->right = tmp;
invertTree(root->left);
invertTree(root->right);
return root;
}
};

自己的交换代码如下:

1
2
3
4
5
void swapNode(TreeNode* left, TreeNode* right){
TreeNode *tmp = left;
left = right;
right = tmp;
}

在invertTree函数中使用该函数,交换两个二叉树指针不能成功交换,而用c++自带的swap函数确又可以。

分析问题

函数传参有三种:值传递,引用传参和指针传参。三者的主要区别为:值传参只传递实参的拷贝值,函数中的操作不影响实参,引用传参和指针传参均会影响实参,函数执行完毕相当于实参完成了函数内部的操作。引用传参和指针传参的不用在于,引用传参必须要先初始化实参,而指针传参不必。以交换变量为例,三中传参分别为:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
//值传参
void swap(int a,int b) {
int tmp = a;
a = b;
b = tmp;
}
//引用传参
void swap(int &a,int &b) {
int tmp = a;
a = b;
b = tmp;
}
//指针传参
void swap(int *a,int *b) {
int tmp = *a;
*a = *b;
*b = tmp;
}

第一种不能完成实参的交换,后两种可以。这样分析下来,自己写的swapNode(TreeNode left, TreeNode right)函数看似也是指针传参呀,但是为何不行呢。这里就出现了偏差,TreeNode* 虽然是指针,但是被交换的两个变量也都是指针,相当于进行了值传参。如果想要真正让交换函数起到作用,应该进行引用传参或者指针传参。

解决方法

引用传参交换两个TreeNode*指针变量表示为:

1
2
3
4
5
void swapNode(TreeNode*& left, TreeNode*& right){
TreeNode *tmp = left;
left = right;
right = tmp;
}

如果有TreeNode a和TreeNode b,直接调用swapNode(a, b)即可。

指针传参交换两个TreeNode*指针变量表示为:

1
2
3
4
5
void swapNode(TreeNode** left, TreeNode** right){
TreeNode *tmp = *left;
*left = *right;
*right = tmp;
}

如果有TreeNode a和TreeNode b,直接调用swapNode(&a, &b)即可。

者两种传参方式传递树指针时,可能同时出现了*或者同时出现了&略显怪异,但是事实确实如此。