Primer 第六章 函數

2020-08-12 23:33:19

函數經典定義(tradition function definition)

返回型別(return type)、函數名字、由0或多個形參(parameter)組成的列表以及函數體(function body)

執行函數

  • 通過呼叫運算子(call operator) 形式是一對圓括號,作用於表達式
    • 圓括號內是一個用逗號隔開的實參(argument)列表
  • 用實參初始化函數的形參
  • 呼叫表達式的型別就是函數的返回型別

函數的呼叫

  • 用實參初始化函數對應的形參(此時形參只是實參的副本)
  • 將控制權轉移給被呼叫函數

函數的返回型別不能是陣列或者函數,但可以是指向陣列或函數的指針
形參和函數內部定義的變數統稱爲區域性變數(local variable),僅在函數的作用域內可見,同時區域性變數還會隱藏(hide)在外層作用域中同名的其他所有宣告中

自動物件(automatic object)
只存在於塊執行期間的物件 例如形參

  • 對於普通區域性變數對應的物件來說,當函數的控制路徑經過變數定義語句時建立該物件,當到達定義所在的塊末尾時銷燬它。
  • 當塊的執行結束後,塊中建立的自動物件的值就變成未定義的了

local static object區域性靜態物件
在程式的執行路徑第一次經過物件定義語句時初始化,直到程式終止才被銷燬

static物件再次遇見初始化語句會忽略初始化語句,不會有重複定義錯誤

函數宣告(declaration)==函數原型(prototype)
separate compilation
Using .cpp files to generate .o files. And then link the .o files

部分概念

如果形參是參照型別,將系結到對應的實參上;否則,將實參的值拷貝後賦給形參 (都是對形參初始化,初始化形式不一樣)
當形參是參照型別時,對應參數被參照傳遞(passed by reference) 或函數被傳參照呼叫(called by reference)
當實參的值被拷貝給形參時,形參和實參是兩個互相獨立的物件。 稱爲實參被值傳遞(passed by value)或者函數被傳值呼叫(called by value)

如果函數無需改變參照形參的值,最好將其宣告爲常數參照

C風格字串末尾新增空字元是爲了方便處理字串

void print(const int*)
void print(const int[])
void print(const int[10])
這三個函數等價

管理陣列實參

  • 要求陣列本身包含一個結束標記
  • 傳遞指向陣列首元素和尾後元素的指針
  • 專門定義一個表示陣列大小的形參
    傳遞多維陣列(陣列的陣列) 首元素本身就是一個數組,指針就是一個指向陣列的指針
    int *matrix[10] 指針陣列 [10][] decltype(m[i])==int *
    int (*matrix)[10] 陣列指針[][10] 行指針

initializer_list

可變參數初始化列表儲存列表

/* initializer_list形參(模板)  
數量不清楚,型別相同 */  
initializer_list<T>lst; //預設初始化:T型別元素空列表  
initializer_list<T>lst{a,b,c...}; /*列表元素是const*/  
lst2(lst)||lst2=lst /*拷貝後,原始列表和副本共用元素*/  
lst.size()
lst.begin()
lst.end()

省略符形參 便於C++程式存取某些特殊的C程式碼而設定 使用varargs的C標準庫功能
形式

  • void foo(parm_list,…); 指定foo函數的部分形參型別 形參宣告後的comma可省
  • void foo(…); 省略符所對應的實參無須型別檢查

return

return 語句終止當前執行的函數並將控制權返回到呼叫該函數的地方
兩種形式

  • return;
  • return expression;

void函數的最後一句後面會隱式地執行return
可以在void函數中間利用return語句可以直接退出函數(類似於break於回圈)
void函數如果使用return的第二種形式,expression 必須是另一個返回void的函數

編譯器儘量確保具有返回值的函數只能通過一條有效的return語句退出

列表初始化返回:返回一個初始化列表

        return {"parameter","list"};

返回陣列指針的函數形式

Type(*function(parameter_list))[dimension]
int (*func(int i))[10];
  • func(int i)表示呼叫func函數時需要一個int型別的實參
  • (*func(int i))意味着我們可以對函數呼叫的結果執行解除參照操作
  • (*func(int i))[10]表示解除參照func的呼叫將得到一個大小是10的陣列
  • int (*func(int i))[10]表示陣列的元素是int型別

Trailing return type

任何函數的定義都能使用尾置返回型別,但這種形式對於返回型別比較複雜的函數最有效(陣列的指針或陣列的參照)
尾置返回型別跟在形參列表後面並以一個->符號開頭
(函數定義) auto func(int i) -> int (*) [10] 返回陣列指針

decltype使用

overload

  • main不能過載
  • 同名函數的使用
  • 不可以只是返回型別不同
  • 不要將過載的函數放入區域性作用域
    編譯器只會在上一作用域尋找過載函數(如果有)(沒有的話就再向上走)
  • int func(int)int func(const int)會重複宣告 (頂層const的有無對於形參的區分沒有影響)
  • int func(int&)int func(const int&)會發生函數過載(底層const影響形參的區分)

function matching

function resolution

  • best match
  • no match
  • ambiguous call(二義性呼叫)

C++中,名字查詢發生在型別檢查之前

預設實參(default argument)

  1. 在宣告時設定預設值
    1. 可再次宣告,但必須新增預設實參(無法覆蓋之前的預設值)
    2. 區域性變數不能作爲預設實參
  2. 呼叫函數時實參順序填充,有預設值的形參可以不使用實參

inline

內聯說明只是向編譯器發出的一個請求,編譯器可以選擇忽略這個請求
適用於短而常用的函數

constexpr

  • 用於常數表達式的函數
  • 函數的返回型別及所有形參的型別都得是字面值型別
  • 函數體中必須有且只有一條return語句
  • constexpr函數的返回值可以是非常數

行內函式和constexpr函數可以在程式中多次定義,但多次定義語句必須完全一致,所以通常定義在標頭檔案中

偵錯幫助

assert (preprocessor macro)

assert*(expr);
首先對
expr*求值,如果表達式爲假(0),assert輸出資訊並終止程式的執行。如果表達式爲真(!0),assert什麼也不做

assert(expr);

assert宏定義在 cassert 標頭檔案中//預處理名字由前處理器管理

NDEBUG

assert 的行爲依賴於 NDEBUG 預處理變數的狀態
如果定義了 NDEBUG ,則assert 什麼也不做
必須將#define NDEBUG 置於 <cassert> 標頭檔案之前
可以使用 NDEBUG 編寫自己的條件偵錯程式碼

func 輸出當前偵錯的函數的名字
FILE 存放檔名的字串字面值
LINE 存放當前行號的整形字面值
TIME 存放檔案編譯時間的字串字面值
DATE 存放檔案編譯日期的字串字面值

函數匹配

候選函數

可行函數

  • 形參數量與本次呼叫提供的實參數量相等
  • 每個實參的型別與對應的形參型別相同,或者能轉換成形參的型別

最佳匹配排列順序

  1. 精確匹配
  2. 通過const轉換實現的匹配
  3. 通過型別提升實現的匹配
  4. 通過算術型別轉換或指針轉換實現的匹配
  5. 通過類型別轉換實現的匹配

函數指針

宣告一個可以指向該函數的指針,只需要用指針替代函數名
int *pr(int)//返回值爲int指針的函數
int (*pr)(int)//函數指針
將函數名作爲一個值使用時,該函數自動地轉換成指針(& 此符可選)
函數指針的過載必須清晰界定選用的函數
函數指針賦值需要精確匹配
函數指針形參
形參可以是指向函數的指針(看起來是函數型別,其實是函數指針)
返回指向函數的指針

  • 返回型別不會自動地轉換成指針(必須顯式地返回型別指定爲指針)
  • 尾置返回型別 (->)返回型別
  • 用 auto 和 decltype(返回函數型別) 簡化書寫函數指針返回型別
using F = int(int *,int);//function type
using PF = int(*)(int *,int);//pointer of function
F *f1(int);
//PF f1(int) 等價形式
//int (*f1(int))(int *,int);
//auto (*f1(int))(int *,int);
int (*(*f)(int))(int *,int)=f1;//函數指針 之前函數的指針
auto func(para_list)->Type(*)(para_list)

參考書籍 C++ Primer 第五版