在函数调用的时候,parameters被创建和初始化,初始化操作使用传递来的arguments完成。
Arguments are the initializers for a function’s parameters.
这里先要理解两个概念,对于一个func
函数:
int func(int a){
return a
}
int b = 0;
func(b);
- a称为parameter
- b称为argument
parameters的初始化与变量的初始化过程相同,因此在函数初始化中会遇到:
- 直接copy数据
- 绑定到argument的reference
这两种方式也分别对应两种函数传递方式:
- pass arguments by value
- pass arguments by reference
Pass arguments by value
基本使用
这种参数传递方式,有以下2个特点:
- 传入函数内的argument会被copy为parameter;
- 在函数内对于copy的参数parameter的写操作,不影响外部的argument;
这种函数传递方式是最简单的一种,例如上述的func(int a)
就是这种参数传递类型。
pointer作为函数参数
按理说,pointer类型的函数参数与其他的类型没啥不同,但是由于pointer指向一个变量的地址,因此会有一些不同的玩法,导致起到和pass arguments by reference的效果。
void func(int *ip) {
*ip = 100;
ip = nullptr;
}
int b = 10;
func(&b);
执行上述代码后,b = 100, ip变成空指针,但是不影响b的地址。而通过,更改 *ip
可以起到按引用传递的效果。
但是在C++中更推荐使用下文中的pass arguments by reference,而不是这种指针的用法。
Pass arguments by reference
这种参数传递方式的特点是:
- 允许在函数内部改变argument中的数据;
- 不需要进行copy操作;
用reference作为参数避免copy
函数传递中的问题:
It can be inefficient to copy objects of large class types or large containers. More-over, some class types (including the IO types) cannot be copied.
针对该问题,使用按引用传值,避免了资源不必要的copy。
bool isShorter(string &s1, string &s2) {
return s1.size() < s2.size();
}
用reference作为参数返回更多信息
函数传递中的另一个问题:
A function can return only a single value. However, sometimes a function has more than one value to return
利用按引用传递,可以返回函数内部的多个操作结果,一般有两种方式:
- 一个是定义多个parameters;
- 另一个是定义数据结构,如结构体、类等;
const
Parameters & Arguments
top-level const的问题
在之前关于const的总结中提到了top-level const和low-level const:
- top level const:对象自己是const;
- low level const:对象指向的对象是const;
在函数参数传递时,关于top-level const有几点注意事项:
top-level const on parameters are ignored.
void func(const int i){ return i; } const int a = 1; int b = 2; func(a); // ok func(b); // ok, top-level const is ignored.
虽然top-level const被忽略了,但是在func中不能修改ii;
void func(const int ii){ ii = 100; // error } // error: assignment of read-only parameter ‘ii’
top level const不影响函数类型,在函数指针的总结中提到了函数类型,由参数类型和返回值类型决定,因此相同函数名也视为不同的函数,但是top level const可以被忽略,因此不会对函数参数类型产生影响;
void func1(const int i){} void func1(int i){} // error: redefinition of ‘void func1(int)’
因此,上述两个func1不是函数重载,而是完全一样。
当const遇到Pointer & Reference
既然函数参数的初始化与变量的初始化一致,因此也有一些通用的指导规则:
- 对于有low level const的对象,可以通过非const对象来初始化,但是反过来不行;
- 引用类型的parameters初始化必须由相同类型的arguments完成;
void func(int &r){}
void func(int *p){}
// for every call in the following
int i = 0;
const int ci = i;
string::size_type ctr = 0;
func(&i); // ok,调用2th func
func(&ci); // error, 不能使用pointer to const int初始化 pointer to int,违反了low-level const的原则
func(i); // ok,,1st func
func(ci); // error,不能使用const int初始化plain reference
func(42); // error, 不能使用literal初始化plain reference
func(ctr); // error, mismatched type
推荐:使用referecne to const作为函数parameters
在函数参数中使用按引用传递参数的方式,可能会导致以下问题:
- 暗示函数调用者,可以改变该引用绑定的值,引起错误;
- 限制了能传入到该参数中的arguments的类型;
如上例所示,调用func(int &r)
时:- 不能传入42等字面值;
- const int类型;
- 或者size_type等可以自动转换的类型;
出现:error: cannot bind non-const lvalue reference of type ‘int&’ to an rvalue of type ‘int’
基于上述问题,因此在函数传递时,推荐使用reference to const。
void func(const int &i){}
参考资料
- C++ Primer - 6.2.1 Passing Arguments by Value
- C++ Primer - 6.2.2 Passing Arguments by Reference
- C++ Primer - 6.2.3 const Parameters and Arguments