本篇文章將整理出關於參數缺省和引用的一些知識。
(一)參數缺省:參數缺省包括全缺省和半缺省。顧名思義,全缺省就是函數的所有參數都給出默認值,半缺省就是僅有函數的部分參數給出了默認值。看下邊的一段代碼:
#include<iostream> using namespace std; int Add(int x, int y) { return x + y; } int main() { int ret = Add(); system("pause"); return 0; }
要是寫出上邊的一段代碼,低版本的vs會編譯不通過,報錯是Add函數不接收0個參數,高版本vs會有紅色標註。要是對於函數調用的時候不想給出參數,我們可以使用默認參數,若將Add函數寫成這樣就好了:
int Add(int x = 0, int y = 0) { return x + y; }
這就是所謂的全缺省。那麼半缺省呢??繼續實例:
int Add(int x , int y = 0) { return x + y; } int main() { int ret = Add(10); system("pause"); return 0; }
這樣子的話,最後會得到10和0相加。當然調用的時候,我們也可以給出兩個參數。如果我們將x給出默認,y不默認,看可以不??
int Add(int x = 0 , int y) { return x + y; }
要是Add函數寫成上邊這樣,高版本vs編譯器會紅線報錯:默認參數不在參數列表末尾。想想這是爲什麼?其實,想一下就明白了,調用的時候(當然實參是隻有一個)會把給出的實參賦值給函數參數列表的第一個形參,肯定就不對了嘛。要是用更專業的說法解釋,本人認爲:函數參數入棧的時候是實參進入Add函數的棧幀,直接就當作第一個參數,所以~總結:使用半缺省,缺省的參數只能在參數列表的最後邊。缺省參數的用途:假如要傳性別這個參數給函數,我們又約定缺省爲男,所以,只需要在需要傳的性別是女時,給出參數。這就是缺省參數的用途。
(二)引用
引用就是給一個變量起一個別名。比如:
int a = 10; int &ra = a;//ra是a的別名 int &refa = a;//refa也是a的別名
總結:
一個變量可以有多個別名。
定義引用的時候必須初始化(指明定義的引用是哪個變量的別名)。
引用只能在初始化的時候引用一次。
既然說,一個變量可以有多個別名,改變了別名的值,也就改變了變量的值,是不是感覺引用就很不安全了??其實並不是,如果不想改變,可以使用const修飾。
int a= 10; const int &ra = a; ra = 20;//報錯!
關於const,在c中const修飾的變量是常變量,既有常量的屬性,也有變量的屬性。在c++中,const修飾的變量就是常量。不可修改。在學習指針的時候,如果一個函數的功能僅僅是打印或者是某個參數不可以被改變的時候,我們可以用const修飾。在使用引用的時候也是一樣的。舉例:
void print_a(const int &ra) { ra = 20;//報錯(左值必須是可以修改的) cout << ra << endl; } int main() { int a = 10; int &ra = a; print_a(a); system("pause"); return 0; }
在cpp中,如果const修飾的是局部變量,則該局部變量是在棧裏。不是不可以修改,可以使用指針修改。如果const修飾的是全局變量,不使用的時候是不會被分配空間的(&n是錯誤的),只有在使用的時候纔有地址。
const int n = 10; int main() { int a = 10; int &ra = a; int arr[n] = { 0 };//這裏並不算使用n,此時的n仍相當於宏標識符 const int *p = &n;//n纔有空間,並且是隻讀區,不可修改的。 system("pause"); return 0; }
關於const引用的正確使用:看下邊的幾段代碼:
const int n = 10; int &refa = n;
此時 refa是可以改變的,進而n的值可以改變。這就不對了嘛。(n是不可以改變的)。使得n從安全變成不安全。
int m = 10; const int &refm = m;
這個就是正確的。使得m從不安全變得安全。
const int &refd= 5;
這個是正確的。const修飾的refd是一個常量的引用。
double d = 6.15; int &refd = d;
這個是不對的。要是這樣就對了:
double d = 6.15; const int &refd = d;
爲什麼呢??由於引用的類型是int,變量的類型是double,所以此時就會用double類型的d創建一個臨時變量(也是d的隱式類型轉換),refd引用的是臨時變量。確切的說就是臨時常量。此時refd =d,refd和d不在一個空間。引用作爲函數參數:這裏需要聲明:引用的底層仍然是指針(這個可以通過彙編代碼觀到)。 下邊我們來測試傳引用和傳值得效率問題:
#include<iostream> #include<windows.h> using namespace std; struct BigData { int arr[100]; }; int getData(BigData bd) { return bd.arr[0]; } int main() { BigData bd = { 0 }; int i = 0; int start = GetTickCount(); for (i = 0;i < 100000000;i++) { getData(bd); } int end = GetTickCount(); cout << end - start << endl; system("pause"); return 0; }
代碼測試出的結果是6000毫秒左右。注意:每次運行的結果都不大一樣,這根CPU的狀態有關。當把上邊代碼改爲傳引用:效率可以提高大約一半,自己可以嘗試。引用作爲函數的返回值:看代碼:
int& fun() { int num = 10; return num; } int main() { int &ret = fun(); cout << ret << endl; system("pause"); return 0; }
上邊這段代碼確實可以輸出10.不過要是把上邊的代碼做以修改:
int& fun() { int num = 10; return num; } int main() { int &ret = fun(); printf("楊先生你好"); cout << ret << endl; system("pause"); return 0; }
看上邊的代碼,在高版本的vs下輸出:楊先生你好10在低版本環境下ret的值會被改變。因爲ret接收的是局部變量num,兩者佔用同一個空間當num消失了,ret也就消失了,變成隨機值。高版本vs(比如vs2015)爲什麼ret的值一直是10,不知道做了什麼優化。。如果將上邊的代碼做以修改:
int& fun() { int num = 10; return num; } int main() { int ret = fun(); printf("楊先生你好"); cout << ret << endl; system("pause"); return 0; }
這個ret的值不會改變。看原理:後者的ret就不是引用了,保存的僅僅是num的值,所以不會隨着num的消失而消失。當引用作爲函數的返回值的時候,有時會提高效率,有時並不會~~我們知道,當函數的返回值,int或者double等等內置類型時,返回值是用寄存器帶回的。然而最大的寄存器也就32位,要是我們要返回一個比較大的結構體時,會使用引用,引用不會創建臨時變量,直接把需要帶回的值賦值給接收返回值的變量。
總結:用函數的返回值的類型不是內置類型時,使用引用返回,會提高效率(減少創 建臨時對象)。當返回的是內置類型時,會用寄存器帶回,跟使用引用返回的效率差不多。
引用和指針的區別:
1.指針在定義的時候可以不初始化,但是引用不行。
2.引用只能引用一個變量,而一般的指針變量並不是(除了const修飾的指針)
3.指針的大小僅與平臺有關,而引用的大小與引用的對象的類型有關。
4.指針自加時,使得指針指向當前指向空間的下一個空間;引用自加時,會使自身和它所引用的對象的值加1.5.引用比指針更安全。(使用指針時,一定要檢查指針的值是否是NULL)