函数的形参一共分为两种:
- 当形参类型是引用类型的时候,我们说它对应的实参被引用传递或者说函数被传引用调用。
- 当形参类型是非引用类型的时候,我们说它对应的实参被传值传递或者是或函数被传值调用。
传值参数
当初始化一个非引用类型的变量的时候,初始值被拷贝给变量。这时候对变量进行的操作不会影响初始值:
int n = 0;
int i = n; //i是n的副本
i = 42; //i的值改变,n的值不变
传值参数的机理完全是一样的,函数对形参做的所有的操作都不会影响实参,比如:
int fact(int val)
{
int ret = -1;
while(val > 1)
ret *= val--;
return ret;
}
在上述代码中,尽管fact函数改变了val的值,但是这个改动并不会改变传入fact的实参。
指针形参
指针的行为和其他非引用类型一样。但是我们要知道,指针的实质是指向某个变量的地址,尽管我们对指针的形参的修改不会影响到实参,但是我们可以通过指针修改它所指向的对象的值:
int n = 0, i = 42;
int *p = &n, *q = &i; //p指向n,q指向i
*p = 42; //n的值改变,但是p不变
p = q; //p是q的副本,p指向i,但是i和n的值都不变
指针形参也是类似:
void reset(int *ip)
{
*ip = 0; //改变ip所指向的对象的值
ip = 0; //改变了局部ip的拷贝,实参没有被改变
}
在上述代码当中,实参所指的对象变为0,但是实参本身并没有改变。
传引用参数
就像我们知道的那样,对于引用的操作实际上是作用在引用所绑定的对象上,下面就让我们看一下代码:
void reset(int &i)
{
i = 0; //改变了i所引对象的值
}
当我们调用上述版本的reset()函数的时候,i绑定我们传给函数的int对象,此时改变i的值就是改变i所引对象的值。
使用引用避免拷贝
当我们拷贝大的类型对象或者容器对象的时候比较低效,甚至有的类型根本不支持拷贝操作,例如IO类型,这时候我们只能用引用形参来访问该类型对象。
比如,我们准备编写一个函数接受两个string类型的时候,想比较他们的长度,这时候我们拷贝的话效率过低,可以考虑传引用,这时候我们知道我们并不需要修改原实参,所以我们可以使用对常量的引用:
bool isShorter(const string &s1, const string &s2)
{
return s1.size() < s2.size();
}
使用引用形参返回额外的信息
我们知道,一个函数只能返回一个值,然而有时候函数需要同时返回多个值,引用形参可以巧妙的解决这个问题。
例如,我们需要返回string对象中某个指定字符第一次出现的位置,同时也想要返回字符出现的总次数:
string::size_type find_char(const string &s, char c, string::size_type &occurs)
{
auto ret = s.size();
ocurrs = 0;
for(decltype(ret) i = 0; i != s.size(); ++i)
{
if(s[i] == c)
{
if(ret == s.size())
ret = i;
++ocurrs
}
}
return ret;
}
上述代码return了字符出现的位置,但是我们的形参有一个引用类型的:occurs,他可以直接让我们知道字符出现字数。
网友评论