C++Primer_ch6

第6章 函數


6.1 函數基礎
6.2 參數傳遞
6.3 返回類型和return語句
6.4 函數重載
6.5 特殊用途語言特性
6.6 函數匹配
6.7 函數指針


6.1 函數基礎

  • 典型的函數包括:返回類型函數名字0或多個形參組成的列表函數體
  • 形參和實參. 實參是形參的初始值,類型匹配
  • 函數的形參列表.函數的形參列表可以爲空,但是不能省略。定義一個不帶形參的函數,可以書寫空的形參列表,也可以使用關鍵字void表示函數沒有形參func(void)
  • 函數返回類型.void返回類型表示函數不返回任何值。函數的返回類型不能是數組類型或者函數類型,但可以是指向數組或者函數的指針
  • 名字有作用域,對象有生命週期。形參和函數體內部定義的變量統稱爲局部變量。同時局部變量還會隱藏在外層作用域中同名的其他聲明中。局部變量的生命起依賴於定義的方式。
  • 自動對象.當函數的控制路徑經過變量定義語句是創建該對象,當到達定義所在的塊末尾時銷燬它,我們把只存在於塊執行期間的對象稱爲自動對象。形參是一種自動對象
  • 局部靜態對象.在程序的執行路徑第一次經過對象定義語句時初始化,並且直到程序終止才銷燬,在此期間即使對象所在的函數結束執行也不會對它有影響
  • 函數聲明.函數名字必須在使用之前聲明。函數只能定義一次,但是可以聲明多次。函數聲明不包含函數體,用一個分號(;)代替。函數三要素:返回類型、函數名、形參類型。函數聲明也稱作函數原型
  • 建議變量或函數在頭文件聲明,在源文件中定義
  • 分離式編譯允許我們把程序分割到幾個文件中,每個文件獨立編譯
#假設fact函數定義位於一個名爲fact.cc的文件中,它的聲明位於Chapter6.h的頭文件中。fact.cc應該包含Chapter6.h的頭文件。另外在名爲factMain.cc的文件中創建main函數,main函數中將調用fact函數。要生成可執行文件,必須告訴編譯起我們用到的代碼在哪裏,編譯過程如下:
$ CC factMain.cc fact.cc
$ CC factMain.cc fact.cc -o main
#修改了其中一個源文件,只需要重新編譯改動的文件。分離式編譯
$ CC -c fact.cc
$ CC -c factMain.cc
$ CC factMain.o fact.o
$ CC factMain.o fact.o -o main

6.2 參數傳遞

  • 形參初始化的機理與變量初始化一樣.形參的類型決定了形參與實參交互的方式。如果形參是引用類型,它將綁定到對應的實參上;否則將實參的值拷貝後賦給形參。當形參是引用類型時,它對應的實參被引用傳遞或者函數被傳引用調用。和其他引用一樣,引用形參也是它綁定的對象的別名。當實參的值被拷貝給形參時,形參和實參是兩個相互獨立的對象,實參被值傳遞或者函數被傳值調用
  • 如果函數無須改變引用形參的值,最好將其聲明爲常量引用
  • 使用引用形參返回額外的信息
  • const形參和實參.和其他初始化過程一樣,當用實參初始化形參時會忽略掉頂層const。當形參有頂層const時,傳給它常量對象或者非常量對象都是可以的。
  • 指針或引用形參與const.可以使用非常量初始化一個底層的const對象,但是反過來不行;同時一個普通的引用必須用同類型的對象初始化
  • 儘量使用常量引用.把函數不會改變的形參定義成普通的引用是一種比較常見的錯誤,這麼做帶給函數調用者一種誤導,即函數可以修改實參的值
  • 數組形參.數組的兩個性質:不允許拷貝數組、使用數組時通常會將其轉換成指針。爲函數傳遞一個數組時,實際上傳遞的是指向數組首元素的指針。如果傳給print函數的是一個數組,則實參自動地轉換成指向數組首元素的指針,數組大小對函數的調用沒有影響。因爲數組時以指針的形式傳給函數的,所以一開始函數並不知道數組的確切大小,需要提供額外信息。管理指針形參有三種常用的技術:使用標記指定數組長度(要求數組本身包含一個結束標記)、使用標準庫規範(傳遞指向數組首元素和尾後元素的指針)、顯式傳遞一個表示數組大小的形參。當函數不需要對數組元素執行寫操作的時候,數據形參指向const指針;只有當函數確實要改變元素值的時候,才把形參定義成指向非常量的指針
void print(const int*);
void print(const int[]);
void print(const int[10]);
//等價
int i=0,j[2]={0,1};
print(&i);  // &i類型是int*
print(j);   //j轉換成int* 並指向j[0]
  • 數組引用形參.
f(int &arr[10]);    //錯誤;將arr聲明成引用的數組
f(int (&arr)[10]);  //正確;arr是具有10個整數的整數數組的引用
  • 傳遞多維數組.數組的數組
int *matrix[10];    //10個指針構成的數組
int*matrix)[10]; //指向含有10個數組的指針
//matrix指向數組的首元素,該數組的元素是由10個整數構成的數組
void print(int (*matrix)[10],int rowSize) {}
//等價定義
void print(int matrix[][10],int rowSize){}
  • main:處理命令行選項.
//給main傳參,假定main函數位於可執行文件prog之內
//這些命令行選項通過兩個可選的形參傳遞給main函數

prog -d -o ofile data0

int main(int argc,char *argv[]) {}
//第一個形參argc表示數組中字符串的數量,第二個形參argv是一個數組。因爲第二個形參是數組,所以main函數也可以定義成:
int main(int argc,char **argv){}

//當實參傳給main函數之後,argv第一個元素指向程序的名字或者一個空字符串,接下來的元素依次傳遞命令行提供的實參。最後一個指針之後的元素值保證爲0.
argv[0] = "prog";
argv[1] = "-d";
argv[2] = "-o";
argv[3] = "ofile";
argv[4] = "data0";
argv[5] = 0;
//當使用argv中的實參時,可選實參從argv[1]開始
  • 含有可變形參的函數.兩種方法:如果所有的實參類型相同,可以傳遞名爲initializer_list的標準庫類型;如果實參類型不同,可以編寫特殊的函數,即可變參數模版,詳細見16章。還有一種特殊的形參類型,即省略符,可以用它傳遞可變數量的實參。
    initializer_list:如果函數的實參數量未知但是全部實參的類型都相同。是一種標準庫類型,用於表示某種特定類型的值的數組,initializer_list類型定義在同名的頭文件中。和vector一樣,initializer_list也是一種模版類型,與vector不同的是,initializer_list對象中的元素永遠是常量值,無法改變。
    在這裏插入圖片描述
void error_msg(initializer_list<string> il)
{
	for (auto beg=il.begin();beg!=il.end();++beg)
		cout<<*beg<<" ";
	cout<<endl;
 
}

if (expected != actual)
	error_msg({"functionX",expected,actual});
else
	error_msg({"functionX","okay"})

省略符形參:是爲了C++程序訪問某些特殊C代碼而設置的。

void foo(param_list, ...);
void foo(...);

6.3 返回類型和return語句

  • return語句終止當前正在執行是函數並將控制權返回到調用函數的地方。兩種形式:return; return expression;
  • 無返回值函數.沒有返回值的return語句只能用在返回類型是void的函數中。返回void函數不要求非得有return語句,因爲在這類函數的最後一句會隱式地執行return。void函數中可以使用return語句,中間位置退出,類似於break
  • 有返回值函數.只要函數返回類型不是void,則函數內的每條return必須返回一個值,返回類型必須與函數的返回類型相同,或者能隱式地轉換成。
  • 返回值.返回的值用於初始化調用點的一個臨時量,該臨時量就是函數調用點結果。如果函數返回引用,則該引用僅是它所引用對象的一個別名
  • 不要返回局部對象的引用或指針.函數完成後,它所佔的存儲空間也隨之被釋放掉,函數終止意味着局部變量的引用將指向不再有效的內存區域
  • 返回類類型的函數和調用運算符.如果函數返回指針、引用或類的對象,我們就能使用函數調用的結果訪問結果對象的成員
  • 引用返回左值.調用一個返回引用的函數得到左值,其他返回類型得到右值。特別是,能爲返回類型是非常量引用的函數的結果賦值
  • 列表初始化返回值.如果列表爲空,臨時量執行值初始化;否則返回的值由函數的返回類型決定
    在這裏插入圖片描述
    -主函數main的返回值.允許main函數沒有return語句直接結束,編譯器會隱式地插入一條返回0的return語句。main函數的返回值可以看做狀態指示器。返回0表示執行成功,返回其他值表示執行失敗,其中非0的值具體含義依機器而定。爲了使返回值與機器無關,cstdlib頭文件定義了兩個預處理變量,可以使用這兩個變量分別表示成功與失敗:
int main()
{
	if(some_failure)
		return EXIT_FAILURE; //定義在cstdlib頭文件中
	else
		return EXIT_SUCCESS; //定義在cstdlib頭文件中
}
//因爲它們是預處理變量,所以既不能在前面加上std::也不能在using聲明中出現
  • 遞歸.如果一個函數調用了它的自身,不管這種調用是直接的還是間接的,都稱爲該函數爲遞歸函數。函數將不斷地調用自身直到程序棧空間耗盡爲止。main函數不能調用自己
  • 返回數組指針.數組不能拷貝,函數不能返回數組,不過可以返回數組的指針或者引用。
    在這裏插入圖片描述
  • 聲明一個返回數組指針的函數.
    在這裏插入圖片描述
  • 使用尾置返回類型.任何函數的定義都能使用尾置返回,但是這種形式對於返回類型比較複雜的函數最有效,比如返回類型時數組指針或者數組引用。尾置返回類型跟在形參列表後面並以一個->符號開頭
//func接受一個int類型的實參,返回一個指針,該指針指向含有10個整數的數組
auto func(int i) -> int(*)[10]

  • 使用decltype.如果知道函數返回的指針將指向哪個數組,就可以使用decltype關鍵字聲明函數返回類型。
    在這裏插入圖片描述

6.4 函數重載

  • 如果同一作用域內的幾個函數名字相同但是形參列表不同,稱爲重載函數。當調用函數時,編譯器會根據傳遞的實參類型推斷選擇的函數。main函數不能重載

  • 不允許兩個函數除了返回類型外其他所有的要素都相同

  • 重載和const形參.一個擁有頂層const的形參無法和另一個沒有頂層const形參區分開來。另一方面,如果形參時某種類型的指針或引用,則通過區分其指向的是某常量對象還是非常量對象可以實現函數重載,此時const是底層。非const可以轉換成const,所以下面四個函數都能作用於非const對象或者指向非const對象的指針
    在這裏插入圖片描述

  • const_cast和重載
     const_cast<string&>
    在這裏插入圖片描述
    在這裏插入圖片描述

  • 調用重載的函數.把函數調用與一組重載函數中某一個關聯起來,叫函數匹配,也叫重載確定。調用重載函數時有三種可能的結果:
    最佳匹配
    無匹配的錯誤信息
    二義性調用

  • 當調用函數時,編譯器首先尋找對該函數的聲明,一旦在當前作用域找到了,編譯器就會忽略外層作用域中的同名實體。在C++中,名字查找發生在類型檢查之前
    在這裏插入圖片描述


6.5 特殊用途語言特性

  • 默認實參.默認實參作爲形參的初始值出現在形參列表中。一旦某個形參被賦予了默認值,它後面所有的形參都必須有默認值。函數調用時實參按其位置解析,默認實參負責填補函數調用缺少的尾部實參。通常,應該在函數聲明中指定默認實參,並將聲明放在合適的頭文件中。局部變量不能作爲默認實參,除此之外,只要表達式的類型能轉換成形參所需的類型,該表達式就能作爲默認實參
  • 內聯函數.內聯函數可避免函數調用的開銷,通常就是將它在每個調用點上“內聯地”展開。
    在這裏插入圖片描述
    在shorterString函數的返回類型前面加上關鍵字inline,這樣就可以將它聲明爲內聯函數
    在這裏插入圖片描述在這裏插入圖片描述

-constexpr函數. 是指能用於常量表達式的函數。定義constexpr函數與其他方法類似,不過函數的返回類型及所有形參的類型都得是字面值類型,而且函數體中必須有且只有一條return語句。爲了能在編譯過程中隨時展開,constexpr函數被隱式地指定爲內聯函數。constexpr函數不一定返回常量表達式
在這裏插入圖片描述

  • 對於某個給定的內聯函數或者constexpr函數來說,它的多個定義必須完全一致。基於這個原因,內聯函數和constexpr函數通常定義在頭文件中

  • 調試幫助.程序可以包含一些用於調試的代碼,但是這些代碼只在開發程序時使用。當應用程序編寫完成準備發佈時,要先屏蔽掉調試代碼,這種方法要用到兩項預處理功能:assertNDEBUG
    assert是一種預處理宏。所謂預處理宏其實是一個預處理變量,有點類似於內聯函數,assert宏使用一個表達式作爲它的條件:assert(expr);對expr求值,若干表達式爲假,assert輸出信息並終止程序的執行,如果爲真,什麼也不做。assert宏定義在cassert頭文件中。預處理名字由預處理器而非編譯器管理
    NDEBUG.assert的行爲依賴於一個名爲NDEBUG的預處理變量的狀態。如果定義了NDEBUG,則assert什麼也不做。默認狀態下沒有定義NDEBUG,此時assert將執行運行時檢查。
    在這裏插入圖片描述
    _ _ func _ _ 輸出當前調試的函數名字。
    在這裏插入圖片描述


6.6 函數匹配

  • 確定候選函數和可行函數. 候選函數具有兩個特徵:一是被調用的函數同名,二是其聲明在調用點可見。可行函數也具有兩個特徵:一是形參數量與本次調用提供的實參數量想等,二是每個實參的類型與對象的形參類型相同,或者能轉換成形參的類型
  • 尋找最佳匹配基本思想是,實參類型與形參類型越接近,它們匹配得越好
f(int,int)
f(double,double)
// (42,2.56)
//二義性

  • 實參類型轉換

6.7 函數指針

  • 函數指針指向的是函數而非對象。想要聲明一個可以指向該函數的指針,只需要用指針替換函數名即可
    在這裏插入圖片描述
    在這裏插入圖片描述
    在這裏插入圖片描述

在這裏插入圖片描述

發佈了37 篇原創文章 · 獲贊 12 · 訪問量 2萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章