【C++】函式的形參
前言
這些天又在複習C++,溫故知新,每次看書都會發現一些之前被自己忽視掉的知識點,所以,學習是不能止步的!
作為一種程式語言,C++最重要的兩個部分就是函式和變數,這兩者之間進行溝通便是通過引數傳遞,而引數傳遞有很多需要注意的細節,今天就來講講引數傳遞的問題。
總的來說,引數傳遞的過程,就是初始化函式形參的過程。
按值傳遞
按值傳遞是最直接也是最容易理解的引數傳遞方式,其形式如下;
void fun(int x,int y){...表示式;} //定義; fun(a,b); //呼叫
當進行呼叫的時候,將引數的值通過拷貝賦值給形參,形參的值在函式體內進行各種計算時,並不影響函式外實參的值,形參值的改變與實參無關;
傳值過程是這樣的;
int y = b; int x = a;
下面這個例子就說明了這點;
#include<iostream> using namespace std; void swap(int, int); void main() { int a(5), b(10); swap(a, b); cout <<"a="<< a << " , "<<"b=" << b << endl; system("pause"); return; } void swap(int x, int y) { int temp; temp = x; x = y; y = temp; cout << "按值傳參" << endl; cout <<"x="<< x << " , " <<"y="<< y<<endl; }
雖然a與b的值被傳遞進入了swap(),在swap內,形參的值進行交換,但是外部a與b的值並沒有交換。
注:形參的建立是自右往左的,也就是先建立y,再建立x,而函式結束時,刪除的順序與之相反。
按地址傳遞(指標形參)
按地址傳遞是用變數的地址初始化形參,這時候的形參是一個指標,其形式如下;
void fun(int* x,int* y){...表示式;} //定義;
fun(&a,&b); //呼叫;
在傳參過程中,形參初始化的操作如下;
int *y = &b; int *x= &a;
這時候重新定義swap()函式,會發現a,b的值被修改了,因為,函式通過指標修改了指標指向的物件的值,這就修改了a,b本身。
#include<iostream>
using namespace std;
void swap1(int*, int*);
void main()
{
int a(5), b(10);
swap1(&a, &b);
cout <<"a="<< a << " , "<<"b=" << b << endl;
system("pause");
return;
}
void swap1(int* x, int* y)
{
int temp;
temp = *x; *x = *y; *y = temp;
cout << "按地址傳參" << endl;
cout << "*x=" << *x << " , " << "*y=" << *y << endl;
}
按引用傳遞
眾所周知,對於引用的操作就是對物件本身的操作,引用是被引用物件本身的一個“別名”,按引用傳遞引數與引用類似,形參成為了實參的一個“別名”,它的形式如下;
void fun(int &x,int &y){...表示式;} //定義;
fun(a,b); //呼叫;
形參初始化的操作如下;
int &y = b; int &x= a;
可以想見,當按引用傳值時,在函式中修改形參的值時,實參的值也被修改了,事實也正是如此。
#include<iostream>
using namespace std;
void swap2(int&, int&);
void main()
{
int a(5), b(10);
swap2(a, b);
cout <<"a="<< a << " , "<<"b=" << b << endl;
system("pause");
return;
}
void swap2(int& x, int& y)
{
int temp;
temp = x; x = y; y = temp;
cout << "按引用傳參" << endl;
cout << "x=" << x << " , " << "y=" << y << endl;
}
優勢
避免進行拷貝操作
雖然在我們的例子中都用的是簡單的int型別,但是在實際使用中,常常需要傳遞很大的容器物件,或者是大的類型別物件,這些時候需要呼叫拷貝建構函式進行傳參,效率低下,通過引用可以很好的避免這一點,大大地節約計算時間和計算資源。
用於返回資訊
我們知道,一個函式一次只能返回一個值,當需要通過函式知道多個變數的資訊是,僅僅有返回值是不夠的,引用形參為我們提供了很好的渠道,通過引用可以獲得函式計算後的各個值,甚至不需要接收函式的返回值。
陣列形參
與普通變數一樣,陣列也可以作為函式的形參。但是在《【C++】細說C++中的陣列之“靜態”陣列》中我們提到過,陣列是不允許被拷貝的,因此我們不能以傳值的形式使用陣列形參,所以改換成傳地址的方式向函式傳遞陣列實參的資訊。
當且僅當用於函式頭或者函式原型時,int *arr與int arr[]是含義相同的,他們都意味著arr是一個指標。所以下面三者是等價的;
int* multi(int a[],int m);
int* multi(int *a,int m);
int* multi(int a[10],int m);
將陣列作為形參時,其實是將陣列首元素的地址,包含元素的種類,以及元素的數目提交給了函式,有了這些資訊後,函式就可以使用陣列的資訊了。但是,也有一些問題需要注意,當我們有一個數組時,可以獲得陣列的大小(利用sizeof()函式),但是當它轉化成指標後,我們沒法獲得它的大小,所以常常顯示地向函式傳遞陣列的大小;
#include<iostream>
using namespace std;
int* multi(int a[], int, int);
void main()
{
int arr[10] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 0 };
int *arr1 = multi(arr,10,10);
cout << arr1[3]<<endl;
system("pause");
return;
}
int* multi(int a[],int len,int m)
{
for (int i = 0; i < len;i++)
{
a[i] *= m;
}
return a;
}
陣列引用形參
變數可以定義成陣列的引用,形參也同樣可以,
int* multi(int (&a)[10],int m);
//注:&a兩端的括號不能少,因為[]的優先順序更高;C++也不允許引用的陣列。其次,必須給定陣列的大小,這在一定程度上限制了函式的使用。
多維陣列作為形參
與陣列一樣,多維陣列也可以作為形參傳遞給函式,與一維陣列一樣,傳遞的也是陣列首元素的地址。因為處理的是陣列的陣列,所以首元素本身是一個數組,指標指向的是一個數組,因此第二維的陣列的大小不能被省略。
int* multi(int (*a)[10],int m);
//注:*a兩端的括號不能掉,因為[]的優先順序更高如果失去了括號,那麼int *a[10]表示的是包含十個int型別指標的陣列。而前者表示a是一個指標,指向含有十個元素的陣列。
帶預設值的形參
很多情況下都要用到預設值的形參,例如OpenCV中的很多函式都使用了預設形參,這樣使得函式更加靈活好用。
當函式提供了形參的預設值,形參就不是必須要從實參獲得值,那麼在函式呼叫時,也不是必須提供與形引數一致的實參。
形參與實參匹配是從左到右的,第一個形參與第一個實參匹配,第二個形參與第二個實參匹配。因此,指定預設值的形參必須放在形參列表的最右側,或者說,在帶預設值的形參的右邊,不允許出現無預設值的形參;
int add(int x = 10, int y = 20){...表示式;}
add();
add(a);
add(a, b);
float f1(float a,float b=1,float c,float d=0); //錯誤
float f1(float a,float b,float c=1,float d=0); //正確
這裡也只是簡單的列舉了一下C++中形參和引數傳遞的內容,還有很多複雜的內容需要在專案實踐中去了解,但是萬變不離其宗,這些東西是我們必須掌握的。
已完。。