谈谈函数调用
【导读】
本篇文章讲述的是函数调用时,如何使用参数、返回类型。
首先,给出三个经常被举出来的例子:
1 #include <iostream> 2 3 using namespace std; 4 5 void testSwap_val(); 6 void testSwap_ptr(); 7 void testSwap_quote(); 8 9 void swap_val(int a, int b) // 传值调用 10 { 11 int temp = a; 12 a = b; 13 b = a; 14 } 15 16 void swap_ptr(int *a, int *b) // 传地址调用 17 { 18 int temp = *a; 19 *a = *b; 20 *b = temp; 21 } 22 23 void swap_quote(int &a, int &b) // 直接绑定实参 24 { 25 int temp = a; 26 a = b; 27 b = temp; 28 } 29 30 31 int main() 32 { 33 testSwap_val(); 34 testSwap_ptr(); 35 testSwap_quote(); 36 return 0; 37 } 38 39 void testSwap_val() 40 { 41 int m = 2, n = 5; 42 swap_val(m, n); 43 cout << m << " " << n << endl; 44 } 45 46 void testSwap_ptr() 47 { 48 int m = 2, n = 5; 49 swap_ptr(&m, &n); 50 cout << m << " " << n << endl; 51 } 52 53 void testSwap_quote() 54 { 55 int m = 2, n = 5; 56 swap_quote(m, n); 57 cout << m << " " << n << endl; 58 }
不用运行程序,我们都知道结果如下:
我们通过分析这个例子,来探讨一下函数调用时的“实参——形参”的关系。
1. 调用函数时,如果被调用函数有参数,会用传入的实参对形参进行初始化。形参初始化的机理与变量初始化一样。
- 传值时,创建形参对象,将实参的值拷贝后赋给形参。形参和实参是两个相互独立的对象。
- 传地址时,行为与传值一样,也是创建一个指针形参,然后将实参的值拷贝后赋给形参。但由于传递的值是某个变量的地址,故可通过“*”达到修改变量的目的。
- 传引用时,和其他引用机理一样,引用形参绑定初始化它的实参(不拷贝),即形参只是实参的别名,修改形参也就是修改实参。
2. 向函数传递一个数组
由上面的讨论可知,除了引用类型的参数,其他参数都涉及拷贝。但我们的数组不允许拷贝,故我们无法将数组作为参数向函数传递一个数组。
我们可以向函数传递指向数组首元素的指针,这样的实现类似于传地址。
上面讨论了函数调用是有关于参数的部分,但是只是冰山一角,关于参数部分更多的东西可参考:推荐形参使用常量引用:void func(const T &);、传递可变数量的实参等等。
函数三巨头:返回类型、函数名、参数列表,上面我们讨论了参数列表,下面谈谈返回类型。
1. 返回的值会用于初始化一个临时量,该临时量就是函数调用的结果。
string ss; ss = func(); // 等价于 /* string ss; string temp = func(); // temp就是函数调用的结果 ss = temp; */
2. 可以以列表的形式返回值,而且如果函数返回的是内置类型,则花括号包围的列表最多包含一个值
vector<int> func() { return {1, 2, 3, 4, 5}; // 返回类型为可包容多个值的vector<int> } int main() { vector<int> ivec; ivec = func(); return 0; }
3. 返回类型为引用时,如果不是常量引用,则该函数调用式可作为左值使用
#include <iostream> using namespace std; int& func(int &a) { return a; // 不能返回局部对象的引用 } int main() { int a = 10; func(a) = 100; // 返回非常量引用的函数可以当作左值使用 cout << a << endl; // 输出100 return 0; }