參數(shù)傳遞
形參初始化的機理與變量初始化相同。形參的類型決定了形參和實參的交互方式舶替。
- 引用傳遞 passed by reference,函數(shù)被 傳引用調(diào)用 called by reference 璃饱。形參是引用,綁定到對應(yīng)實參上网持。引用形參也是其綁定對象的別名,即引用形參是對應(yīng)實參的別名长踊。
- 值傳遞 passed by value 功舀,函數(shù)被傳值調(diào)用 called by value ,將實參的值拷貝賦給形參身弊。
傳值參數(shù)
初始化非引用類型的變量辟汰,傳值會拷貝初值,因此對變量的改動不會影響初始值阱佛。(對比于python)
指針形參
當(dāng)執(zhí)行指針拷貝操作帖汞,拷貝的是指針值,即地址值凑术◆嬲海拷貝后兩個指針是不同的指針。通過指針可以修改它所指對象的值麦萤,
指針形參的行為類似指針行為鹿鳖,可以改變實參的值扁眯,例如下:
void reset(int* ip){
*ip = 0; // ip所指對象置 0
ip = nullptr壮莹; // 改變ip本身的局部拷貝,不改變實參
}
int main(){
int i = 42;
reset(&i); //由于是指針形參姻檀,需要取地址傳參命满。
cout<<i<<endl; //i=0
}
建議使用引用類型的形參(C++風(fēng)格)替代指針類型。
傳引用參數(shù)
對引用的操作實際上是作用在引用所引的對象上绣版。引用形參類似胶台。通過引用形參可以改變一個或多個實參的值。
void reset(int& i){
i = 0;
}
int main(){
int i = 42;
reset(i);
cout<<i<<endl; //i=0
}
使用引用避免拷貝
對較大類型對象和容器對象以及不支持拷貝的對象杂抽,為了性能或者可行性诈唬,只能通過引用形參訪問該類型的對象。
如比較兩個string對象的長度缩麸。由于string對象可能非常長不便于拷貝铸磅,而又不需要在函數(shù)中修改時,可以利用對常量的引用定義形參:
bool isShorter(const string& s1, const string& s2){
return s1.size()<s2.size();
}
使用引用形參返回額外信息
C++函數(shù)只能有一個返回值杭朱。若要返回多個且有可能不同類型的值阅仔,可以構(gòu)造一個含有這些值類型的新類,或者直接傳入一個引用類型的形參并隱式返回該值弧械。
例如該程序同時統(tǒng)計在字符串中某個字符的首次出現(xiàn)位置索引和總次數(shù)八酒,可以編寫如下:
string::size_type find_char(const string& s, char c,
string::size_type& occurs){
auto ret = s.size();
occurs = 0;
for(decltype(ret)i=0; i!=s.size(); ++i){
if (s[i]==c){
if (ret == s.size()) ret = i;
++occurs;
}
}
return ret;
}
int main(){
string::size_type occ;
string s = "bcdefghahaha";
string::size_type r;
r = find_char(s, 'a', occ);
cout<<"First sub: "<<r<<";\nTotal occurs: "<<occ;
return 0;
}
輸出:
First sub: 7;
Total occurs: 3
const 形參和實參
僅在用實參初始化形參時形參具有的頂層const會被忽略。因此可以傳給其常量和非常量刃唐。而只能給底層const的形參傳遞常量羞迷。
因此界轩,雖然C++允許定義同名而擁有不同形參列表的函數(shù),但下述兩個定義會被認(rèn)為是同一個衔瓮,盡管函數(shù)內(nèi)的形參功能不同:
void a(const int i){}
void a(int a){}
指針或引用形參參與const
具體的底層耸棒、頂層及可更改對象和初始化規(guī)則一致。
盡量使用常量引用
將函數(shù)內(nèi)不會改變其值的形參定義為常量引用报辱,既可以同時接受const和非const類型的實參与殃,又可以避免不需要的問題。
- 問題1:非底層const引用無法使用字面值初始化碍现。
void a(string a, int b){} void b(const string a, const int b){} a("str", 1) //error b("str", 1) //correct
- 問題2:不同函數(shù)嵌套時幅疼,若內(nèi)層有函數(shù)是普通引用而無法接受const類的實參,會出錯昼接。
數(shù)組形參
數(shù)組的兩個特殊性質(zhì)對定義和使用作用在數(shù)組的函數(shù)有影響:
-
不允許拷貝數(shù)組(如不可以
a=b
等):無法以值傳遞的方式使用數(shù)組參數(shù) - 使用數(shù)組時可能會被轉(zhuǎn)化為指針:為函數(shù)傳遞數(shù)組時實際上是傳遞了指向該數(shù)組首元素的指針爽篷。
因此,下列三個表達(dá)式等價慢睡,形參類型都是const int*
逐工。
void a(const int*);
void a(const int[]); //本意是作用與數(shù)組,實際形參是一個指針漂辐。
void a(const int[10]); //10僅僅是期望的元素泪喊,實際不一定。
存在的問題:由于無法拷貝且會轉(zhuǎn)為指針髓涯,當(dāng)確實需要數(shù)組作為實參時袒啼,開始和結(jié)束位置難以確定。
使用標(biāo)記指定數(shù)組長度
管理數(shù)組實參的第一種方法:要求數(shù)組本身包含一個結(jié)束標(biāo)記纬纪。典型示例是c風(fēng)格字符串(以'\0'結(jié)尾)的處理:
void print(const char* cp){
if (cp) //指向內(nèi)容不為空
while (*cp) //所指字符不是空字符
cout<<*cp++;
}
使用標(biāo)準(zhǔn)庫規(guī)范
管理數(shù)組實參的第二種方法:傳遞指向數(shù)組首元素和尾后元素的指針蚓再,注意需要在使用函數(shù)前使用std::beg
或std::end
提前取得這兩個指針:
void print(const char* beg, const char* end){
while (beg!=end)
cout<<*beg++;
}
顯式傳遞表示數(shù)組大小的形參
管理數(shù)組實參的第三種方法:定義一個表示數(shù)組大小的形參。在舊版本的 C++ 和 C 中常用這種方法包各。注意該形參的類型是size_t
摘仅。
數(shù)組形參和const
和引用形參一樣,盡量將指針形參其定義為const類型避免不必要的問題问畅。
數(shù)組引用形參
若將變量定義為數(shù)組的引用娃属,可以直接使用。但是注意引用構(gòu)成的數(shù)組(X)-int& arr[10]
和 對數(shù)組的引用-int (&arr)[10]
按声。
傳遞多維數(shù)組
一般第一個維度使用指針傳遞膳犹,而操作必不可少的第二個維度,只能傳入其含有的元素個數(shù):
void print(int (*matrix)[10], int rowSize){}
// 等價于
void print(int matrix[][10], int rowSize){}
int matrix[][10]
看似是個二維數(shù)組的聲明签则,實際上聲明了一個指向含有10個整型元素的數(shù)組的指針须床。與 int (*matrix)[10]
一致。
main:處理命令行選項
往main()函數(shù)中定義參數(shù)以設(shè)置運行選項渐裂,如將這些命令傳遞給prog文件中的程序:prog -d -o ofile data0
:
int main(int argc, char* argv[]){}
第二個形參argv是一個數(shù)組豺旬,數(shù)組內(nèi)每項都是char指針钠惩;第一個形參表示字符串?dāng)?shù)量。第二個形參是數(shù)組族阅,所以main函數(shù)也可以定義為:
int main(int argc, char** argv){}
其中 argv
指向 char*
篓跛。其內(nèi)容(數(shù)組)或指向的內(nèi)容(指針):
argv[0] = "prog"; //程序名
argv[1] = "-d"; //開始參數(shù)
argv[2] = "-o";
argv[3] = "ofile";
argv[4] = "data0";
argv[5] = 0;
含有可變形參的函數(shù)
常常會無法提前預(yù)知向函數(shù)傳遞的實參數(shù)量。為了使函數(shù)處理未知數(shù)量實參坦刀,C++11提供兩種方法:
- 所有實參類型相同:傳遞名為
initializer_list
的標(biāo)準(zhǔn)庫類型愧沟; - 實參類型不同:可變參數(shù)模板
initializer_list 形參
expression | description |
---|---|
initializer_list<T> lst; |
默認(rèn)初始化空列表 |
initializer_list<T> lst{a, b, c...}; |
列表初始化,內(nèi)部元素都是const |
lst2(lst) |
賦值鲤遥,前后二者共享元素 |
lst2 = lst |
拷貝沐寺,前后二者共享元素 |
lst.size() |
列表中元素數(shù)量 |
lst.begin() |
返回指向lst首元素的指針 |
lst.end() |
返回指向lst尾后元素的指針 |
initializer_list對象中的元素永遠(yuǎn)是常量值,無法改變盖奈。
使用begin和end成員可以完成類似vector的迭代器操作混坞。
-
向該類型形參中傳遞一個值的序列,必須把序列放在一對花括號內(nèi):
initializer_list<string> str = a? {"A", "B"}:{"C", "D", "E"};
含有該類型形參的函數(shù)也可以含有其他類型的形參
省略符形參
省略符形參是為了便于C++程序訪問某些特殊的C代碼設(shè)置的钢坦,這些代碼使用了名為varargs
的C標(biāo)準(zhǔn)庫功能究孕。通常省略符形參不應(yīng)用于其他目的。
只能出現(xiàn)在形參列表最后一個位置爹凹,但注意大多數(shù)類類型的對象無法正確拷貝給該形參厨诸。