天天看點

C++_函數重載&引用&内聯函數

文章目錄

    • 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、特性:

  1. 預設參數必須從右往左給出;
  2. 不能在函數聲明和定義的位置同時給出;
  3. 預設參數在提供時—常量||全局變量;
  4. 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++中,一般情況下傳參時盡量使用引用。

内置類型:

  1. 如果需要通過形參改變外部的實參,盡量傳遞引用;
  2. 如果不需要通過形參改變外部的實參,使用傳值、傳引用+const

自定義類型:

    傳參都是用引用, T& 和 const T&

3.4、指針和引用的差別

    說明:引用和指針在底層實作方式是完全相同的,引用就是按照指針的方式來實作的。

    在概念和特性以及使用方式上的差別:

  1. 引用概念上定義一個變量的别名,指針存儲一個變量位址;
  2. 引用在定義時必須初始化,指針沒有要求;
  3. 引用在初始化時引用一個實體後,就不能再引用其它實體,而指針可以在任何時候指向任何一個同類型實體;
  4. 沒有NULL引用,但有NULL指針;
  5. 在sizeof中含義不用,引用結果為引用類型的大小,但指針始終是位址空間所占位元組個數(32位平台下占4個位元組);
  6. 引用自加即引用的實體增加1,指針自加即指針向後偏移一個類型的大小;
  7. 有多級指針,但沒有多級引用;
  8. 通路方式不同,指針需要解引用,引用編譯器自己處理;
  9. 引用比指針使用起來相對更安全。

4、内聯函數

4.1、宏常量

    優點:一改全改,代碼的可維護性高;常量名字具有一定的含義—#define MAX_SIZE 100

    缺點:宏常量沒有類型的,不會參與到類型檢測中,代碼的安全性降低,而且一旦編譯報錯,報錯的位置不準确。

4.2、宏函數

    優點:宏函數不是一個真正的函數,在預處理階段,預處理器已經将宏函數進行替換了,少了函數調用參數壓棧,開辟棧幀,傳回等的開銷了,代碼的運作效率提高。

    缺點:

  1. 在實作時,可能會比較麻煩,要到處加括号;
#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; }
           
  1. 宏函數也沒有參數類型,不會有參數類型檢測,安全性不高;
  2. 宏函數在預處理階段會展開,不能調試;
  3. 宏函數具有副作用。
#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函數具有檔案作用域

繼續閱讀