C++ 函式(一)引數傳遞/return語句/返回型別
C++ 函式(一)引數傳遞/return語句/返回型別
6.1 基礎
函式的返回值不能是陣列或函式型別,但可以是指向陣列或函式的指標
6.1.1 作用域,區域性物件
函式體是一個塊,構成一個新的作用域,其中定義的變數和形參都是區域性變數
在函式體外部定義的物件存在於程式的整個執行過程中
自動物件:
只存在於塊執行期間的物件,例如形參,函式終止,形參會被銷燬,區域性變數如果本身不帶初始值,則會執行預設初始化,意味內建型別的未初始化區域性變數將產生未定義的值
區域性靜態物件
定義為static型別,如果沒有顯式的初始值,則將初始化為0
函式宣告
又叫函式原型,不包含函式體,無需形參名(也可以有)
6.2 引數的傳遞
pass by value
將實參的值拷貝後賦值給形參,對形參的操作不會影響實參
指標形參
傳遞指標,拷貝的是指標的值,拷貝後的兩個指標是不同的指標,但是由於指標可以間接的訪問所指的物件,所以可以通過指標修改它所指的物件值
void reset(int *p){
*p = 0;//改變了指向的物件的值
p = 0;//只改變了p的區域性拷貝,實參未改變
}
C++建議用引用型別的形參代替指標
pass by reference
void reset(int &p){}
改變引用形參,就是改變初始化形參的實參
儘量使用引用來避免拷貝,而且有些類不支援拷貝(比如IO類)
6.2.2 const形參,const實參
頂層const作用於物件本身,形參有頂層const時,傳遞常量或非常量物件都可以(實參初始化形參時會忽略頂層const)
void func(const int n){}
const int i = 1;
int j = 2;
func(i);
func(j);
//上面兩種呼叫方法都正確
//由於這種忽略,導致下面兩種函式其實是一樣的,所以不是過載
void func(const int i);
void func(int i);
6.2.3 指標,引用形參
可以使用非常量初始化一個底層const,但是不能反過來,一個普通的引用必須用同類型的物件初始化
int i = 0; const int ci = i; string::size_type ctr = 0; reset(&i);//形參型別為int* reset(ci);//錯誤,不能使用const int物件的指標去初始化int* reset(i);//形參為int&時 reset(ci);//不能將普通引用繫結到const物件ci上 reset(42);//不能繫結字面值 reset(ctr);//型別不匹配,ctr是無符號型別
可以使用字面值初始化常量引用
儘量使用常量引用,避免限制函式能接收的實參型別(字面值)
6.2.4 陣列形參
陣列的兩個特殊性質:
- 不能拷貝陣列:無法值傳遞
- 使用陣列時通常轉化成指標:傳遞引數時,實際上傳遞的是指向陣列首元素的指標
陣列的大小對函式呼叫沒影響
管理指標形參:(主要是長度問題)
一種方法是使用標記指定長度,只適用於C語言中類似C風格字串,以空字元結束
所以更實用的是另外兩種方法:
-
標準庫函式begin,end
void print(const int *beg,const int *end){ while(beg!=end){ cout<<*beg++<<endl; } } int j[2] = {0,1}; print(begin(j),end(j));
-
顯式傳遞一個數組大小形參
print(j,end(j)-begin(j));
陣列引用形參/const形參
不需要改變陣列元素值時,最好加上const
//陣列的引用,維度是型別的一部分
void print(int (&arr)[10]){}
//必須加(),否則arr就是引用的陣列,而不是具有10個整數的整型陣列的引用
//傳遞引數時,只能將函式作用域大小為10的陣列
int i[2] = {2,1};
print(i);//錯誤
多維陣列
C++的多維陣列就是陣列的陣列
陣列的陣列,首元素本身就是一個數組,第二維不能省略
void print(int (*matrix)[10],int rowsize);
//如果沒有(),則是十個指標構成的陣列,而不是指向含有10個整數的陣列的指標
void print(int matrix[][10],int rowsize);
6.2.5 main(int argc,char *argv[])
6.2.6 可變形參
處理不同數量的實參的函式:
-
如果所有實參的型別相同,可以傳遞標準庫型別:initializer_list
-
不同則寫可變引數模板
-
省略符(一般只用於與C函式互動的介面程式)
形式:
void fun(parm_list,...)
void fun(...)
僅用於C和C++通用的型別
initializer_list物件中的元素永遠是常量值
拷貝,賦值一個initializer_list物件不會拷貝列表中的元素,拷貝後原始列表和副本共享元素
list2(list1);//拷貝
list2 = list1;//賦值
void error_msg(initializer_list<string> il){
//initializer_list有begin,end,所以可以使用指標,範圍for迴圈遍歷列表
}
//呼叫:expected,actuall為string物件
error_msg({"functionX",expected,actuall});
6.3 返回值 (return)
無返回值的return後可以什麼都沒有,也可以跟另外一個返回void的函式
不要返回區域性物件的引用或指標
函式結束後,區域性變數的記憶體空間會被釋放
const string&manip(){//下面兩個都錯
string ret;
if(){
return ret;//區域性物件
}
else{
return "empty";//臨時區域性變數
}
}
引用返回左值:
呼叫運算子優先順序和點,箭頭相同
//符合左結合律,返回指標,引用,類的物件:
auto s = func(s1,s2).size();
呼叫返回引用的函式會得到左值,其他型別為右值
因此這種情況下函式的返回值可以像使用其他左值一樣:
char &get_val(string &str,string::size_type ix){
return str[ix];
}
int main()
{
string s("a value");
get_val(s,0) = 'A';
//s[0]被改為A
return 0;
}
//返回值是個常量引用就不能賦值了
列表初始化返回值
vector<string>pro(){
return {"one","two"};
}
如果返回內建型別則{}中最多包含一個值
返回陣列指標
陣列不能拷貝,所以函式不能返回陣列
使用類型別名簡化返回陣列的指標和引用
//下面兩行程式碼是等價的
typedef int arr[10];
using arr = int[10];
//返回一個指向含有十個整數的陣列的指標
arr* func(int i);
//如果不使用類型別名:
Type (*function(parameter_list))[dimension]
int (*func(parameter_list))[10];
//必須有*之外的(),否則宣告的函式返回型別就會是指標的陣列,而不是陣列的指標
尾置返回:
函式真正的返回型別在形參列表之後,格式:
auto func(int i)->int(*)[10];
//任何函式定義都能使用尾置返回
decltype
如果知道函式返回的指標指向哪個陣列,則可以使用decltype
int o[] = {12,3};
int o2[] = {1,2};
decltype(o)* arrptr(int i){}
decltype的結果是陣列,所以還要加上*