函數(shù)的形參一共分為兩種:
- 當形參類型是引用類型的時候,我們說它對應(yīng)的實參被引用傳遞或者說函數(shù)被傳引用調(diào)用写半。
- 當形參類型是非引用類型的時候岸蜗,我們說它對應(yīng)的實參被傳值傳遞或者是或函數(shù)被傳值調(diào)用。
傳值參數(shù)
當初始化一個非引用類型的變量的時候叠蝇,初始值被拷貝給變量璃岳。這時候?qū)ψ兞窟M行的操作不會影響初始值:
int n = 0;
int i = n; //i是n的副本
i = 42; //i的值改變,n的值不變
傳值參數(shù)的機理完全是一樣的蟆肆,函數(shù)對形參做的所有的操作都不會影響實參,比如:
int fact(int val)
{
int ret = -1;
while(val > 1)
ret *= val--;
return ret;
}
在上述代碼中晦款,盡管fact函數(shù)改變了val的值炎功,但是這個改動并不會改變傳入fact的實參。
指針形參
指針的行為和其他非引用類型一樣缓溅。但是我們要知道蛇损,指針的實質(zhì)是指向某個變量的地址,盡管我們對指針的形參的修改不會影響到實參坛怪,但是我們可以通過指針修改它所指向的對象的值:
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的拷貝,實參沒有被改變
}
在上述代碼當中居灯,實參所指的對象變?yōu)?祭务,但是實參本身并沒有改變。
傳引用參數(shù)
就像我們知道的那樣怪嫌,對于引用的操作實際上是作用在引用所綁定的對象上义锥,下面就讓我們看一下代碼:
void reset(int &i)
{
i = 0; //改變了i所引對象的值
}
當我們調(diào)用上述版本的reset()函數(shù)的時候,i綁定我們傳給函數(shù)的int對象岩灭,此時改變i的值就是改變i所引對象的值拌倍。
使用引用避免拷貝
當我們拷貝大的類型對象或者容器對象的時候比較低效,甚至有的類型根本不支持拷貝操作噪径,例如IO類型柱恤,這時候我們只能用引用形參來訪問該類型對象。
比如找爱,我們準備編寫一個函數(shù)接受兩個string類型的時候膨更,想比較他們的長度性誉,這時候我們拷貝的話效率過低茫负,可以考慮傳引用,這時候我們知道我們并不需要修改原實參蚤告,所以我們可以使用對常量的引用:
bool isShorter(const string &s1, const string &s2)
{
return s1.size() < s2.size();
}
使用引用形參返回額外的信息
我們知道,一個函數(shù)只能返回一個值矗漾,然而有時候函數(shù)需要同時返回多個值锈候,引用形參可以巧妙的解決這個問題。
例如敞贡,我們需要返回string對象中某個指定字符第一次出現(xiàn)的位置泵琳,同時也想要返回字符出現(xiàn)的總次數(shù):
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了字符出現(xiàn)的位置,但是我們的形參有一個引用類型的:occurs誊役,他可以直接讓我們知道字符出現(xiàn)字數(shù)获列。