文章目錄
-
- 1、預設參數
-
- 1.1、概念
- 1.2、分類:
- 1.3、特性:
- 2、函數重載
-
- 2.1、概念
- 2.2、調用原理
- 2.3、底層實作原理
- 2.4、extern "C"
- 3、引用
-
- 3.1、概念
- 3.2、特性
- 3.3、三種傳參方式差別
- 3.4、指針和引用的差別
- 4、内聯函數
-
- 4.1、宏常量
- 4.2、宏函數
- 4.3、C++如何解決宏的缺陷
1、預設參數
1.1、概念
在聲明或定義函數時,給函數的參數設定一個預設值,當使用者對函數進行調用時,如果傳遞了實參,則使用使用者傳遞的實參;如果沒有傳遞則使用預設值。
1.2、分類:
- 全預設參數:所有參數都帶有預設值;
- 半預設參數:部分參數帶有預設值。規則:必須從右往左依次給出。
1.3、特性:
- 預設參數必須從右往左給出;
- 不能在函數聲明和定義的位置同時給出;
- 預設參數在提供時—常量||全局變量;
- C語言不支援。
2、函數重載
2.1、概念
相同作用域下,函數名字相同,參數清單必須不同(參數個數、參數類型、類型次序)。
注意:與函數的傳回值類型是否相同沒關系。
2.2、調用原理
編譯器在編譯代碼期間,需要對函數的實參類型進行推演,根據推演的結果選擇對應合适類型的函數進行調用。
注意:有該函數存在,則直接調用,如果不存在類型完全比對的函數,則編譯器會嘗試進行隐式類型轉換,轉換完成後,如果有對應類型的函數,則進行調用,否則:編譯失敗(沒有對應類型、調用二義性)。
2.3、底層實作原理
C++之是以能夠支援函數重載,C語言不支援函數重載,是因為C++編譯器和C語言編譯器對函數名字的修飾規則不同。
注意:不同編譯器對函數名修飾的細節可能不太相同,但大概原理是一樣的。
(1)C語言編譯器對函數名字的修飾規則:隻是在函數名字前加 _
(2)C++編譯器對函數名字的修飾規則:C++編譯器将參數類型放到最終的名字中。例如:g++的修飾規則【_Z+函數長度+函數名+類型首字母】
2.4、extern “C”
在C++出現以前,很多代碼都是C語言寫的,而且很底層的庫也是C語言寫的,為了更好的支援原來的C代碼和已經寫好的C語言庫,需要在C++中盡可能地支援C,而且在正常開發中,有些人擅長C語言,有些人擅長C++,那完全可能出現:C語言和C++混合起來程式設計。
在函數前加“extern “C”,意思就是告訴編譯器,将該函數按照C語言規則來編譯”
extern "C" int Add(int left, int right);
int main()
{
Add(1,2);
return 0; }
3、引用
3.1、概念
引用是一個别名,不是新定義一個變量,編譯器不會給引用變量重新配置設定空間。引用變量與其引用地實體共用同一份記憶體空間。
3.2、特性
- 引用變量在定義時必須要初始化;
- 一個變量可以有多個引用;
- 引用變量一旦引用一個實體之後,就不能再去引用其它的實體。
3.3、三種傳參方式差別
1、傳值:
優點:可以對外部實參起到保護作用。
缺點:不能通過形參改變外部的實參,因為形參是實參的一份拷貝,在函數體中,修改形參實際修改的是實參的一份拷貝.
傳參的效率低下,而且浪費空間。
2、傳位址:
優點:可以通過形參改變外部的實參;不需要對實參進行拷貝,傳參的效率高、節省空間。
缺點:在不需要通過形參改變外部實參的情況下,代碼安全性不高。可以避免:const
指針:不安全,每次在使用時必須要判空,代碼的可讀性比較差。
3、傳引用:
形參是實參的别名,對形參進行修改可以達到對實參的改變,對于不需要通過形參改變外部實參通過:const
傳參效率高、節省空間、代碼可讀性高。
注意:在C++中,一般情況下傳參時盡量使用引用。
内置類型:
- 如果需要通過形參改變外部的實參,盡量傳遞引用;
- 如果不需要通過形參改變外部的實參,使用傳值、傳引用+const
自定義類型:
傳參都是用引用, T& 和 const T&
3.4、指針和引用的差別
說明:引用和指針在底層實作方式是完全相同的,引用就是按照指針的方式來實作的。
在概念和特性以及使用方式上的差別:
- 引用概念上定義一個變量的别名,指針存儲一個變量位址;
- 引用在定義時必須初始化,指針沒有要求;
- 引用在初始化時引用一個實體後,就不能再引用其它實體,而指針可以在任何時候指向任何一個同類型實體;
- 沒有NULL引用,但有NULL指針;
- 在sizeof中含義不用,引用結果為引用類型的大小,但指針始終是位址空間所占位元組個數(32位平台下占4個位元組);
- 引用自加即引用的實體增加1,指針自加即指針向後偏移一個類型的大小;
- 有多級指針,但沒有多級引用;
- 通路方式不同,指針需要解引用,引用編譯器自己處理;
- 引用比指針使用起來相對更安全。
4、内聯函數
4.1、宏常量
優點:一改全改,代碼的可維護性高;常量名字具有一定的含義—#define MAX_SIZE 100
缺點:宏常量沒有類型的,不會參與到類型檢測中,代碼的安全性降低,而且一旦編譯報錯,報錯的位置不準确。
4.2、宏函數
優點:宏函數不是一個真正的函數,在預處理階段,預處理器已經将宏函數進行替換了,少了函數調用參數壓棧,開辟棧幀,傳回等的開銷了,代碼的運作效率提高。
缺點:
- 在實作時,可能會比較麻煩,要到處加括号;
#define MUL(A, B) A*B
int main()
{
int a = 1, b = 2, c = 3, d = 4;
cout<< MUL(a+b, c+d)<<endl; // 預處理階段被替換成:a+b*c+d
return 0; }
- 宏函數也沒有參數類型,不會有參數類型檢測,安全性不高;
- 宏函數在預處理階段會展開,不能調試;
- 宏函數具有副作用。
#define MAX(A, B) (((A) > (B))? (A) : (B))
int main()
{
int a = 10, b = 20;
cout<<MAX(++b, a)<<endl; // 期望輸出21,但實際輸出的是22,因為直接替換後,++b算了兩次
return 0; }
4.3、C++如何解決宏的缺陷
1、宏常量:const常量,可以達到宏替換的效果,而且具有類型,更加安全。
int main()
{
const int a = 10;
int* pa = (int*)&a;
*pa = 100; // 此處已經将a的值修改為100
cout << *pa << endl; // 列印100
cout << a << endl; // 列印10,因為在編譯階段,常量a被替換為10
return 0; }
2、宏函數:解決方案—内聯函數
内聯函數:在C++中被inline關鍵字修飾的函數稱為内聯函數;如果成員函數在類中定義,編譯器也可能會将其當成内聯函數來處理。
特性:
- inline是一種空間換時間的做法,省去調用函數額外開銷。是以代碼很長或者有循環/遞歸的函數不适宜使用作為内聯函數;
- 在編譯階段,如果編譯器将一個函數當成是内聯函數的情況下,在編譯代碼時會對内聯函數進行展開,少了函數調用的開銷,程式的運作效果提高了;
- inline是一個建議性的關鍵字,當修飾函數時,建議編譯器将該函數當成内聯函數來進行處理,即:在編譯階段,将該函數展開。如果定義為内聯函數體内有循環/遞歸等等,編譯器優化時會忽略掉内聯;
- inline不建議聲明和定義分離,分離會導緻連結錯誤。因為inline被展開,就沒有函數位址了,連結就會找不到;
- inline函數具有檔案作用域