引用,是某個已存在變量的另外一個名字。ios
一旦把引用初始化爲某個變量,就可使用該引用名稱或變量名稱來指向變量。編程
注意:函數
引用沒有定義,是一種關係型聲明。聲明它和原有某一變量(實體)的關
系。所以引用類型必須與原類型保持一致,且不分配內存。與被引用的變量有相同的地
址。this
& 符號前有數據類型時,是引用。其它皆爲取地址。spa
可對引用再次引用。屢次引用的結果,是某一變量具備多個別名。指針
建立引用例子以下:code
// reference.cpp #include <iostream> using namespace std; int main () { // 聲明簡單的變量 int i; double d; // 聲明引用變量 int& r = i; double& s = d; i = 1; cout << "Value of i : " << i << endl; cout << "Value of i reference : " << r << endl; d = 6.1; cout << "Value of d : " << d << endl; cout << "Value of d reference : " << s << endl; getchar(); return 0; }
運行結果:對象
引用編程實踐以下:blog
// reference2.cpp #include <iostream> using namespace std; int main() { int a = 1; int& b = a; // b = a = 1 a = 2; int *p = &a; *p = 3; // a = 3 cout << "a = " << a << endl; b = 4; // b = a -> a = 4 cout << "a = " << a << ", b = " << b << endl; getchar(); return 0; }
運行結果:內存
引用很容易和指針混淆,它們之間有如下不一樣:
不存在空引用。引用必須鏈接到一塊合法的內存。指針能夠爲空指針。
引用必須在建立時被初始化(引用做爲函數參數的時候不須要初始化,由於形參必定會被賦值的)。指針能夠在任什麼時候間被初始化。
一旦引用被初始化爲一個對象,就不能被指向到另外一個對象。指針能夠在任什麼時候候指向到另外一個對象。
引用做爲其餘變量的別名而存在,所以在一些場合能夠代替指針
引用相對於指針來講具備更好的可讀性和實用性
// 沒法實現兩數據的交換 void swap(int a,int b); //開闢了兩個指針空間用於交換 void swap(int *a,int *b);
// referenceSwap.cpp,不開闢空間使用引用進行數值交換 #include <iostream> using namespace std; void swap(int& a, int& b) { int temp; temp = a; a = b; b = temp; } int main() { int a = 1,b = 2; cout << "a = " << a << " , b = " << b << endl; swap(a,b); cout << "a = " << a << " , b = " << b << endl; getchar(); return 0; }
運行結果:
全局變量、靜態局部變量、靜態全局變量,new 產生的變量都在堆中,動態分配的變量在堆中分配。
局部變量在棧裏面分配。
程序爲棧變量分配動態內存,在程序結束爲棧變量清除內存,可是堆變量不會被清除。
當函數返回值爲引用時:
以下代碼所示:
// funcReturnRef.cpp,引用做爲函數的返回值,何時能夠爲其餘引用初始化的值 #include <iostream> using namespace std; // 返回棧變量 int getA1() { int a; a = 1; return a; } // 返回棧變量引用 int& getA2() { int a; a = 1; return a; } int main() { int a1 = 0; int a2 = 0; // 值拷貝 a1 = getA1(); // 將 棧變量引用 賦值給 變量,編譯器相似作了以下隱藏操做:a2 = *(getA2()) a2 = getA2(); // 將 棧變量引用 賦值給 另外一個引用做爲初始值。此時將會有警告:返回局部變量或臨時變量的地址 int& a3 = getA2(); cout << "a1 = " << a1<< endl; cout << "a2 = " << a2<< endl; cout << "a3 = " << a3<< endl; getchar(); return 0; }
警告信息:
第一次運行結果:
第二次運行結果:
以下代碼所示:
// funcReturnRef2.cpp,引用做爲函數的返回值,何時能夠爲其餘引用初始化的值 #include <iostream> using namespace std; // 返回堆變量 int getA1() { static int a; a = 1; return a; } // 返回棧變量引用 int& getA2() { static int a; a = 1; return a; } int main() { int a1 = 0; int a2 = 0; // 值拷貝 a1 = getA1(); // 將 棧變量引用 賦值給 變量,編譯器相似作了以下隱藏操做:a2 = *(getA2()) a2 = getA2(); // 將 堆變量引用 賦值給 另外一個引用做爲初始值。因爲是靜態區域,地址不變,內存合法。 int& a3 = getA2(); cout << "a1 = " << a1<< endl; cout << "a2 = " << a2<< endl; cout << "a3 = " << a3<< endl; getchar(); return 0; }
運行結果:
C++ 中指針引用做函數參數,與 C 語言中二級指針做函數參數的區別。
以下代碼所示:
// pointReference.cpp // C++ 中指針引用做函數參數,與 C 語言中二級指針做函數參數的區別 #include <iostream> using namespace std; #define AGE 18 // C 語言中的二級指針 int getAge1(int **p) { int age = AGE; int *ptemp = &age; // p 是實參的地址, *實參的地址,去間接的修改實參 *p = ptemp; return 0; } // C++ 中指針引用 int getAge2(int* &p) { int age = AGE; if(p == NULL) { p = (int *)malloc(sizeof(int)); if(p == NULL) return -1; } // 給 p 賦值,至關於給 main 函數中的 pAge 賦值 *p = age; return 0; } int main(void) { int *pAge = NULL; // 1 C 語言中二級指針 getAge1(&pAge); cout << "age: " << *pAge << endl; pAge = NULL; // C++ 中指針引用 getAge2(pAge); cout << "age: " << *pAge << endl; pAge = NULL; getchar(); return 0; }
運行結果:
const 對象的引用必須是 const 的。
const 引用可使用相關類型的對象(常量,非同類型的變量或表達式)初始化。這個是 const 引用與普通引用最大的區別。
例:
const int &a = 1; double x = 1.1; const int &b = x;
C 語言中有宏函數的概念。宏函數的特色是內嵌到調用代碼中去,避免了函數調用的開銷。可是因爲宏函數的處理髮生在預處理階段,確實了語法檢測和有可能帶來的語義差錯,所以 C++ 引入了 inline 內聯函數。
內聯函數聲明時 inline 關鍵詞必須和函數定義結合在一塊兒,不然編譯器會忽略內聯請求。
C++ 編譯器直接將函數體插入在函數調用的地方。
內聯函數沒有普通函數調用時的額外開銷(壓棧,跳轉,返回)
內聯函數是一種特殊的函數,具備普通函數的特徵(參數檢查,返回類型等)
內聯函數由編譯器處理,直接將編譯後的函數體插入在調用的地方,宏函數由預處理器處理,進行簡單的文本替換,沒有任何的編譯過程。
C++ 對內聯函數的限制:
編譯器對於內聯函數的限制不是絕對的,內聯函數相對於普通函數的優點知識節省了函數調用時壓棧,跳轉,和返回的開銷。所以,當函數體的執行開銷遠大於壓棧,跳轉,和返回的開銷時,那麼內聯函數將沒有意義。
實例代碼以下所示:
// inlineFunction.cpp // 內聯函數示例 #include <iostream> using namespace std; inline void func() { cout << "this is inlineFunction example!" << endl; } int main() { func(); getchar(); return 0; }
運行結果:
函數重載:用同一個函數名定義不一樣的函數,當函數名和不一樣的參數搭配時函數的含義不一樣。
函數名相同
參數個數不一樣,參數的類型不一樣,參數順序不一樣,都可構成重載。
返回值類型不一樣則不能夠構成重載。
以下所示:
void func(int a); // ok void func(char a); // ok void func(char a,int b); // ok void func(int a,char b); // ok char func(int a); // 與第一個函數衝突,報錯
嚴格匹配,找到即調用。
經過隱式轉換尋求一個匹配,找到即調用。
C++ 利用 name mangling(傾軋)技術,來改變函數名,以區分參數不一樣的同名函數。
實現原理:用 v c i f l d 表示 void char int float long double 及其引用。
以下所示:
void func(char a); // func_c(char a); void func(char a,int b,double c); // func_cid(char a,int b,double c);
// 方法一: // 聲明一個函數類型 typedef void (myfunctype)(int a,int b); // 定義一個函數指針 myfunctype* fp1= NULL; // 方法二: // 聲明一個函數指針類型 typedef void (*myfunctype_pointer)(int a,int b) // 定義一個函數指針 myfunctype_pointer fp2 = NULL; // 方法三: // 直接定義一個函數指針 void (*fp3 )(int a,int b);
當使用重載函數名對函數指針進行賦值時,根據重載規則挑選與函數指針參數列表一致的候選者,嚴格匹配候選者的函數類型與函數指針的函數類型。
示例代碼以下所示:
#include <iostream> using namespace std; void func(int a, int b) { cout << a << b << endl; } void func(int a, int b, int c) { cout << a << b << c << endl; } void func(int a, int b, int c, int d) { cout << a << b << c << d << endl; } // 1 定義一個函數類型 typedef void(myfunctype)(int, int); //定義了一個函數類型, 返回值void 參數列表是 int,int ,, void()(int,int) // 2 定義一個函數指針類型 typedef void(*myfunctype_pointer)(int, int); //定義了一個函數指針類型, 返回值void 參數列表是 int,int ,, void(*)(int,int) int main(void) { //1 定義一個函數指針 myfunctype * fp1 = NULL; fp1 = func; fp1(10, 20); // 2 定義一個函數指針 myfunctype_pointer fp2 = NULL; fp2 = func; fp2(10, 20); // 3 直接定義一個函數指針 void(*fp3)(int, int) = NULL; fp3 = func; fp3(10, 20); cout << " -----------------" << endl; // 此時的fp3 是 void(*)(int,int) // fp3(10, 30, 30); // fp3 恆定指向一個 函數入口,void func(int, int) 的函數入口 // fp3(10, 30, 40, 50); // 想要經過函數指針,發生函數重載 是不可能。 fp3(10, 20); void(*fp4)(int, int, int) = func; // 在堆函數指針賦值的時候,函數指針會根據本身的類型 找到一個重載函數 fp4(10, 10, 10); // fp4(10, 10, 10, 10); // 函數指針,調用的時候是不可以發生函數重載的。 void(*fp5)(int, int, int, int) = func; // void func(int ,int ,int ,int ) fp5(10, 10, 10, 10); return 0; }
運行結果:
重載函數在本質上是相互獨立的不一樣函數。
函數重載是由函數名和參數列表決定的。