日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 编程语言 > c/c++ >内容正文

c/c++

C++ primer第六章函数的学习

發(fā)布時間:2023/12/13 c/c++ 33 豆豆
生活随笔 收集整理的這篇文章主要介紹了 C++ primer第六章函数的学习 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

介紹

  • 首先介紹函數(shù)的定義和聲明,包括如何傳入?yún)?shù)以及函數(shù)如何返回結(jié)果。
  • C++語言允許使用重載函數(shù),即幾個不同的函數(shù)可以使用向同一個名字。所以接下來介紹重載函數(shù)的方法,以及編譯器選擇如何從函數(shù)的若干重載的形式中選取一個與調(diào)用模板相互匹配的版本進行使用。
  • 最后介紹一些關(guān)于函數(shù)指針的知識。

6.1 函數(shù)的基礎(chǔ)

  • 一個函數(shù)的定義包括以下幾個部分:返回類型、函數(shù)的名字、0或者多個形參組成的列表或者函數(shù)體。
  • 形參以逗號分隔,形參列表位于一對圓括號里面。
  • 函數(shù)具體實現(xiàn)的代碼寫在一對花括號里面,整體稱之為函數(shù)體。

編寫函數(shù)

  • 寫一個求階乘的函數(shù)
#include<iostream> using namespace std;int fact(int val){int rel = 1;for(int i = val;i != 0;i--){rel *= i;}return rel; }int main(){cout << "The result of rel is "<< fact(5) << endl;return 0; }

調(diào)用函數(shù)

  • 調(diào)用函數(shù)主要完成兩項工作:1,實參初始化函數(shù)對應(yīng)的形參;2,將控制權(quán)交給調(diào)用函數(shù),這個時候,被調(diào)的函數(shù)開始執(zhí)行。

形參和實參

  • 實參數(shù)量和形參數(shù)量、類型一致
  • 如果定義沒有參數(shù)的函數(shù),使用void關(guān)鍵字void f1();// 隱式定義空的形參列表? ? void f2(void); //顯式定義空的形參列表
  • 任意兩個形參不可以同名,而且函數(shù)最外層作用域中的變量也不可以和形參同名。

函數(shù)的返回類型

  • 一種特殊的返回類型是void
  • 函數(shù)的返回類型不可以是數(shù)組或函數(shù)類型,但可以是指向函數(shù)或者數(shù)組的指針。

6.1.1 局部對象

  • 形參和函數(shù)體內(nèi)部定義的變量統(tǒng)一稱為局部變量,對于函數(shù)而言是局部的、是隱藏的。
  • 函數(shù)執(zhí)行的時候創(chuàng)建,函數(shù)執(zhí)行結(jié)束的時候會銷毀的變量叫做自動對象。
  • 對于局部變量對應(yīng)的自動對象來說,如果變量本身含有初始化的數(shù)值,就采用初始化的數(shù)值,否則就采用默認(rèn)的初始化的值。

局部靜態(tài)對象

  • 將局部變量定義成static的類型,這種使用static類型修飾的變量,只會在程序第一次進入的時候進行初始化,直到程序的終止才會銷毀,在此期間,即使對象所在的函數(shù)結(jié)束也不會對它有任何的影響。

6.1.2 函數(shù)聲明

  • 函數(shù)聲明不需要函數(shù)體,也就是無需形參的名字。
  • 函數(shù)的三個要素(返回類型、函數(shù)名字、形參類型)描述了函數(shù)的接口,說明了調(diào)用這個函數(shù)的
  • 一般將函數(shù)的聲明放在頭文件中,就可以確保同一函數(shù)的聲明保持一致,一旦想要改變函數(shù)的接口,只需要改變一條聲明語句即可。
  • 定義函數(shù)的源文件應(yīng)該包含所有函數(shù)聲明的頭文件,這樣編譯器就會負(fù)責(zé)驗證函數(shù)的定義和聲明是否匹配

6.1.3 分離式編譯

  • 將程序拆分成不同的部分分別存儲,分離式編譯器允許將程序分割到幾個文件中,對于每個文件進行獨立編譯。

編譯和鏈接多個源文件

  • 頭文件 存放函數(shù)的聲明
  • 源碼文件 存放函數(shù)的具體實現(xiàn)的代碼
  • 主函數(shù) 調(diào)用函數(shù)的具體執(zhí)行,需要引入頭文件

例子

  • 源文件 factCC.cpp
int fact(int val){int rel = 1;for(int i = val;i != 0;i--){rel *= i;}return rel; }
  • 頭文件 factHead.h
int fact(int val);
  • main函數(shù)
#include<iostream> #include "factHead.h" using namespace std;int main(){cout << "The result is " << fact(5) << endl;return 0; }

6.2 參數(shù)傳遞

  • 每次調(diào)用函數(shù)的時候,都會重新創(chuàng)建它的形參,并用傳入的實參進行初始化
  • 和其他變量一樣,形參的類型決定了形參和實參的交互方式。如果形參是引用類型,它將綁定到對應(yīng)的實參上;否則,將實參的數(shù)值拷貝后賦值給形參。
  • 當(dāng)形參是引用類型時,它對應(yīng)的實參被引用傳遞或者函數(shù)被傳引用調(diào)用。和其他引用一樣,引用形參也是它綁定的對象的別名,即引用形參是他對應(yīng)的實參的別名。
  • 當(dāng)實參的數(shù)值被拷貝給形參的時候,形參和實參是兩個相互獨立的對象。這樣的實參被值傳遞或者函數(shù)被傳值調(diào)用。

6.2.1 傳值參數(shù)

  • 當(dāng)初始化一個非引用類型的變量時,初始值被拷貝給變量,此時對于變量的改動不會影響初始值。
int n = 0;//int類型的初始變量 int i = n;//i是n的副本 i = 42;// 對于i的改變不會影響到n的數(shù)值

指針形參

  • 指針的行為和其他非引用的類型一致。執(zhí)行指針拷貝操作的時候,拷貝的是指針的數(shù)值。拷貝之后,兩個指針是不同的指針。因為指針使我們可以間接地訪問它所指的對象,所以通過指針可以修改它所指的對象的數(shù)值。
int main(){int n=0,i=42;int *p = &n,*q = &i;//p指向n,q指向i*p = 44;//n的數(shù)值改變,p不變p=q;//p現(xiàn)在指向i了,但是i和n的數(shù)值都不變cout << "The n result is " << n << endl; //44cout << "The i result is " << i << endl; //42cout << "The p result is " << *p << endl;//42cout << "The q result is " << *q << endl;//42return 0; } //該函數(shù)接受一個指針,然后將只針?biāo)赶虻奈恢迷O(shè)為0 void reset(int *ip){*ip = 0;// 改變了指針ip所指向的數(shù)值ip = 0;//只改變了ip的局部拷貝,實參未被改變 } int main(){int i = 42;reset(&i);cout << "i = " << i << endl;return 0; }
  • C++建議使用引用類型的形參替代指針

6.2.2 傳引用參數(shù)

  • 對于引用的操作實際上是作用在引用所引的對象上。
int main(){int n = 0,i=42;int &r = n; //r綁定了n,r是n的另外一個名字r = 42; //改變了n的數(shù)值,n也是42cout << "n = " << n << endl;//42cout << "r = " << r << endl;//42return 0; } //該函數(shù)接受一個int對象的引用,然后將對象的數(shù)值設(shè)為0 void reset(int &i){//i是傳給函數(shù)對象的另外一個名字i = 0;//改變了i所引對象的數(shù)值 }int main(){int j = 42;reset(j);cout << "j = " << j << endl;//42return 0; }
  • 和其他引用的類型一致,引用形參綁定初始化他的對象。當(dāng)調(diào)用reset函數(shù)的時,i就會綁定我們傳給函數(shù)的int對象,改變i的數(shù)值也就是改變i引用的數(shù)值。

使用引用避免拷貝

  • 拷貝大的類類型對象或者容器對象比較低效,甚至有的類類型就根本不支持拷貝操作。當(dāng)某種類型不支持拷貝操作時,函數(shù)只能通過引用形參來訪問該類型的對象。
  • 比如一個比較兩個字符串大小的函數(shù),考慮到字符串都比較長,就要避免直接拷貝他們,這個時候使用引用形參就是比較明智的選擇,因為比較長度無法改變string對象的內(nèi)容,所以把形參定義成對于常量的引用。
bool isShorter(const string &s1,const string &s2){return s1.size() < s2.size(); }

使用形參返回額外的信息

  • 使用形參可以一次返回多個結(jié)果提供了有效的途徑。
  • 例子:函數(shù)返回在string中某個字符第一次出現(xiàn)的位置以及該字符總共出現(xiàn)的次數(shù)
  • 如何使函數(shù)既可以返回位置也返回出現(xiàn)的次數(shù)呢?一種方法是定義一個新的數(shù)據(jù)類型,讓他包含位置和次數(shù)兩個成員;另外一種方法是給函數(shù)傳入一個額外的引用實參,另其保存字符出現(xiàn)的次數(shù)
//返回s中c第一次出現(xiàn)的位置索引 //引用形參occurs負(fù)責(zé)統(tǒng)計c出現(xiàn)的總的次數(shù) string::size_type find_char(const string &s,char c,string::size_type &occurs){auto ret = s.size();//第一次出現(xiàn)的位置(如果存在的話)occurs = 0;for(decltype(ret) i = 0;i!= s.size();i++){if(s[i] == c){if(ret == s.size()){ret = i;}++ occurs;}}return ret; }bool isShorter(const string &s1,const string &s2){return s1.size() < s2.size(); } int main(){string s1 = "HelloWorld";string s2 = "Hello";string::size_type ctr;auto index = find_char(s1,'o',ctr);cout << "index = " << index << " ctr = " << ctr << endl;bool s3 = isShorter(s2,s1);cout << "" << s3 << endl;return 0; }
  • 其中,給ret賦值為最大長度的目的是為了在后面判斷的時候,查看是否改變,從而確定是不是第一次遇到這個數(shù)值,可以將大于數(shù)組長度的任意值作為判定的條件。

6.2.3 const形參和實參

  • 當(dāng)形參是const的時候,頂層的const作用于對象的本身。
const int ci = 42; // 不能改變ci,const是頂層的int i = ci; // 正確,當(dāng)拷貝ci的時候,會忽略他的頂層的數(shù)值int * const p = &i;//const是頂層的,不可以給p賦值*p = 0;//正確,通過p改變對象的的內(nèi)容是允許的,現(xiàn)在i的數(shù)值變?yōu)榱?
  • 和其他初始值一樣,當(dāng)使用實參初始化形參的時候會忽略掉頂層的const。即,形參的頂層const被忽略掉了。
  • 當(dāng)形參有頂層的const的時候,傳給他的常量對象或者非常量對象都是可以的。
  • void fun(const int i)//fun可以能夠讀取i,但是不可以向i中寫值
  • void fun(const int i)//錯誤,重復(fù)定義了fun(int) C++允許定義若干具有相同的名字的函數(shù),前提是不同函數(shù)的形參列表應(yīng)該有明確的區(qū)別。此處是因為const被忽略掉了,因IC兩個函數(shù)沒有任何的區(qū)別,不可以重復(fù)的定義。

指針或者引用形參與const

  • 形參的初始化和變量的初始化的方式是一樣的。我們可以使用非常量初始化一個底層的const對象,但是反過來不可以,同時一個普通的引用必須使用相同類型的對象初始化。
int i = 42;const int *cp = &i; //正確,cp不能改變i,const是頂層的const int &r = i; //正確,r不能改變i,const是頂層的const int *r2 = &i; //正確,r2不能改變i,const是頂層的int *p = cp;//錯誤,類型不符int &r3 = r;//錯誤,類型不符int &r4 = 42;//錯誤,不能用字面值初始化一個非常量的引用
  • 將同樣的規(guī)則使用在參數(shù)傳遞上

盡量使用常量引用

  • 把函數(shù)不會改變的形參定義成(普通的)引用是一種常見的錯誤,這么做會給調(diào)用者一種誤導(dǎo),即函數(shù)不會改變它的實參的數(shù)值。使用引用而非常量引用也會極大地限制函數(shù)所能接受的實參類型。
  • 不能把const對象、字面值或者需要類型轉(zhuǎn)換的對象傳遞給普通的引用形參。這種錯誤很難排解
  • 這個是上面提到的代碼,對其進行修改string::size_type find_char(const string &s,char c,string::size_type &occurs),改為string::size_type find_char(const string &s,char c,string::size_type &occurs),將string類型的形參定義成常量引用。假如將其定義成普通的string&,沒有const進行修飾。那么在使用的時候只可以auto index = find_char(“Hello ”,'o',ctr);,編譯會發(fā)生錯誤。
  • 假如其他函數(shù)將他們的形參定義成常量的引用,那么第二個版本的函數(shù)無法在此類函數(shù)上正常使用。假設(shè)在一個判斷string對象是否是句子的函數(shù)中使用find_char;
bool is_sentsence(const string &s){//如果s的末尾有一個句號且僅有一個,則是句子string::size_type ctr = 0;return find_char(s,'.',ctr) == s.size()-1 && ctr == 1; }
  • 如果find_char()第一個形參類型是string & ,那么會發(fā)生編譯錯誤,是因為s是常量的引用,但是函數(shù)find_char被定義成可以只能接受普通引用。如果修改is_sentsence函數(shù)的形參類型只會轉(zhuǎn)移錯誤,使得is_sentsence函數(shù)只可以接受非常量的string對象。
  • 正確的思路是改變find_char的函數(shù)的形參。實在不行才修改is_sentsence函數(shù),在其內(nèi)部定義一個string類型的變量,另其為s的副本,然后把這個string對象傳遞給find_char()。

6.2.4 數(shù)組形參

  • 數(shù)組的兩個特殊的性質(zhì)使得我們定義和使用作用在數(shù)組上的函數(shù)有影響,這兩個性質(zhì)是:1,不允許拷貝數(shù)組;2,使用數(shù)組的時候通常要將其轉(zhuǎn)換為指針,所以當(dāng)為函數(shù)傳遞一個數(shù)組的時候,實際上傳遞的是指向數(shù)組首元素的指針。
  • 雖然不能以數(shù)值的方式傳遞數(shù)組,但是可以形參寫成類似數(shù)組的形式。
//盡管形式不同,但是這三個print的函數(shù)是等價的//每個函數(shù)都會有一個const int*類型的形參void print(const int *);void print(const int[]); //函數(shù)的意圖是作用于一個數(shù)組void print(const int[10]); //這里的維度表示我們期望數(shù)組會含有多少個元素,實際上不一定//盡管表現(xiàn)形式不同,但是上面這三個函數(shù)是等價的,每個函數(shù)的唯一形參都是const int *類型的//當(dāng)編譯器處理對print函數(shù)的調(diào)用的時候,只會檢查傳入的參數(shù)是否是const int *類型的int i = 0,j[2] = {0,1};print(&i); //正確,&i的類型是int *print(j); //正確,j轉(zhuǎn)化成int *并且指向j[0]return 0;
  • 如果傳遞給print函數(shù)是一個數(shù)組,則實參自動轉(zhuǎn)化成指向數(shù)組首元素的指針,數(shù)組的大小對于函數(shù)的調(diào)用沒有關(guān)系,但是在使用數(shù)組的時候需要注意到數(shù)組越界的問題。

使用標(biāo)記指定數(shù)組的長度

  • 要求數(shù)組本身擁有一個結(jié)束的標(biāo)記,典型問題是C風(fēng)格的字符串中,在字符后面跟著一個空的字符。
void print(const char *cp){if(cp) //如果cp不是一個空的指針while(*cp) //只要指針指向的字符不是空的字符串cout << *cp++ ; //輸出當(dāng)前字符的數(shù)值,并將指針向前移動一個位置 } int main(){char s[] = "Hello World!";print(s);return 0; }
  • 這種方法適用于那些有著明顯的結(jié)束標(biāo)記,但是該標(biāo)記不會與普通的數(shù)據(jù)相互混淆的情形,但是不適用于所有的取值都是合法的情形。

使用標(biāo)準(zhǔn)庫規(guī)范

  • 管理數(shù)組實參的第二種方法是傳遞指向數(shù)組首元素和尾后元素的指針
void print(const char *beg,const char *end){//輸出beg到end之間(不包含end)的所有元素while (beg != end){cout << *beg++;} }int main(){char s[] = "Hello";print(begin(s),end(s));return 0; }
  • while循環(huán)使用解引用運算符和后置遞減運算符輸出當(dāng)前元素并且在數(shù)組內(nèi)將beg向前移動一個元素,當(dāng)beg和end指針相等的時候結(jié)束循環(huán)
  • 為了調(diào)用這個函數(shù),需要傳入兩個指針,一個指向要輸出的首元素,一個指向尾元素的下一個位置。具體的調(diào)用方法如上圖所示。此處使用bigin和end函數(shù)提供所需的地址。

顯式傳遞一個表示數(shù)組大小的形參

  • 第三種管理數(shù)組實參的方法是專門定義一個表示數(shù)組大小的形參,在C程序和過去的C++程序中常常使用這種方法。
//const int ia[] 等效于const int* ia //size表示數(shù)組的大小,將它顯式地傳遞給函數(shù)用于控制對ia元素的訪問 void print(const int ia[],size_t size){for(size_t i = 0;i != size;i++){cout << ia[i] << endl;} } int main(){int j[] = {0,1,2,3,4,5,6,7,8,9};print(j,end(j) - begin(j));return 0; }

數(shù)組形參和const

  • 前三個函數(shù)都將數(shù)組的形參定義成指向const的指針,對于引用類型也同樣適用于指針。當(dāng)函數(shù)不需要對于數(shù)組元素執(zhí)行寫操作的時候,數(shù)組的形參應(yīng)該是指向const的指針。只有當(dāng)函數(shù)確實要改變元素數(shù)值的時候,才會把形參定義成指向常量的指針。

數(shù)組引用形參

  • C++允許將變量定義成數(shù)組的引用,基于同樣的道理,形參也是數(shù)組的引用。這個時候,引用形參綁定到對應(yīng)的實參上,也就是綁定到數(shù)組上。
//正確,形參是數(shù)組的引用,維度是類型的一部分 void print(int (&arr)[10]){for(auto elem : arr){cout << elem << endl;} }
  • 因為數(shù)組的大小是構(gòu)成數(shù)組類型的一部分,所以只要不超過維度,在函數(shù)體內(nèi)就可以放心的使用數(shù)組。
  • 但是,如果采用上面的代碼格式就會限制了print函數(shù)的可用性,即只能將函數(shù)作用于大小是10的數(shù)值。

注意事項?

  • &arr的兩端的括號必不可少
  • f(int &arr[10]) //錯誤,將arr聲明成了引用類型
  • f(int (&arr)[10]) //正確,arr是具有10個整數(shù)的整型數(shù)組的引用

傳遞多維數(shù)組

  • C++實際上并沒有多維數(shù)組,所謂的所謂數(shù)組其實是數(shù)組的數(shù)組。
  • 和所有數(shù)組一樣,當(dāng)將多維數(shù)組傳遞給函數(shù)的時候,真正傳遞的是指向數(shù)組首個元素的指針。因為多維數(shù)組就是數(shù)組的數(shù)組,因此首元素就是一個數(shù)組,指針是一個指向數(shù)組的指針。數(shù)組的第二維度以及后面所有的維度的大小都是數(shù)組類型的一部分,不可以忽略。
//matrix指向數(shù)組的首個元素,該數(shù)組的元素是由10個整數(shù)構(gòu)成的數(shù)組void print(int (*matrix)[10],int rowSize){}
  • 上面的語句是將matrix聲明成指向10個整數(shù)的數(shù)組的指針。
//再一次強調(diào),*matrix兩端的括號必不可少int *matrix[10]; //10個指針構(gòu)成的數(shù)組int (*matrix)[10]; //指向含有10個整數(shù)的數(shù)組的指針
  • 也可以使用數(shù)組的語法定義函數(shù),此時編譯器會一如既往地忽略掉第一個維度,所以最好不要把它包括在形參的列表內(nèi),所以最好不要把它包括在形參列表內(nèi)。
  • //等價定義 void print(int matrix[][10] , int rowSize){}
  • matrix的聲明看起來是一個二維數(shù)組,實際上形參是指向含有10個整數(shù)的數(shù)組的指針。

6.2.5 main 處理命令行選項

  • main函數(shù)是演示C++程序如何向函數(shù)傳遞數(shù)組的好例子。到目前為止,使用的是main函數(shù)都是只有空的形參列表的形式。比如int main(){}
  • 有時候確實需要給main函數(shù)傳遞實參,一種常見的情況下是用戶通過設(shè)置一組選項來確定函數(shù)所要執(zhí)行的操作。
  • 例如,假定main函數(shù)位于執(zhí)行prog之內(nèi),我們可以向程序傳遞下面的選項。
  • prog -d -o ofile data0? ?這些命令的選項通過兩個可以選擇的形參傳遞給main函數(shù)
  • int main (int argc, char *argv[]){? ?}
  • 第二個形參argv是一個數(shù)組,他的元素是指向C風(fēng)格的字符串的指針,第一個形參argc表示數(shù)組中字符串的數(shù)量。因為,第二個形參是數(shù)組,所以main函數(shù)也可以定義成
  • int main(int argc,char **argv[]){? ? },其中argv指向char *
  • 當(dāng)形參傳給main函數(shù)之后,argv的第一個元素指向程序的名字或者一個空的字符串,接下來的元素依次傳遞命令行提供的實參。最后一個指針之后的元素數(shù)值保證為0。
  • 當(dāng)使用arrgv中的實參時,一定要記得可選的實參從arrgv【1】開始,arrgv[0]保存程序的名字,并非用戶的輸入

6.2.6 含有可變形參的函數(shù)

  • 一般使用在無法提前預(yù)知向函數(shù)傳遞幾個實參的情形。
  • 為了處理不同數(shù)量實參的函數(shù),C++11提供了兩個主要的方法:1,如果所有的類型都相同,可以傳遞一個名為initializer_list的標(biāo)準(zhǔn)庫類型;2,如果實參的類型不同,可以編寫一個特殊的函數(shù),也就是所謂的可變參數(shù)模板。
  • C++還有一種特殊的形參類型,即省略符號,可以用它傳遞可變數(shù)量的實參。介紹的省略符形參一般只適用于與C函數(shù)交互的接口程序。

initializer_list形參

  • 適用于函數(shù)的實參數(shù)量未知但是全部的實參類型是一致的情形。
  • initializer_list是一種標(biāo)準(zhǔn)庫類型,用于表示某種特定類型的數(shù)值和數(shù)組,其定義在同名的頭文件中。
initializer_list<T>lst; //默認(rèn)初始化,T類型元素的空的列表initializer_list<T>lst{a,b,c...}; //list元素的數(shù)量和初始值一樣多,lst的元素是對應(yīng)初始值的副本;列表中的元素是constlst2(lst); //拷貝或者賦值一個initializer_list對象不會拷貝列表中的元素,拷貝之后原始列表和副本共享元素lst.size();//類表中元素的數(shù)量lst.begin();//返回指向lst中首元素的指針lst.end();//返回指向lst中尾元素下一個未知的指針
  • initializer_list和vector一樣,也是一種模板類型,定義initializer_list的時候也必須說明列表中所包含元素的類型
  • initializer_list<string> ls;? //initializer_list的元素類型是string
  • initializer_list<int> li;? ? //initializer_list的元素類型是int
  • initializer_list中的元素永遠(yuǎn)是常量,這一點不同于vector,因此無法改變initializer_list對象中元素的數(shù)值。
  • 使用如下代碼就可以編寫輸出錯誤信息的函數(shù),使其作用于可以改變的數(shù)量的實參。
void error_msg(initializer_list<string> il){for(auto beg = il.begin();beg != il.end();++beg){cout << *beg << " "<< endl;} }
  • 如果想向initializer_list形參中傳遞一個數(shù)值的序列,則必須將序列放在一對花括號內(nèi)。

省略符形參

  • 省略符形參是為了C++程序訪問特定的C代碼而設(shè)置的,這些代碼使用了名為varargs的C的標(biāo)準(zhǔn)庫的功能。
  • 通常省略形參不應(yīng)該用于其他的目的。
  • 省略形參應(yīng)該用于C和C++通用的類型,但是值得注意的是,大多數(shù)的類類型的對象在傳遞給省略符形參的時候都無法正確的拷貝。
  • 省略形參只能出現(xiàn)在形參列表的最后一個位置,形式無外乎兩種
  • void foo(parm_list,...);指定了foo函數(shù)的部分形參的類型,對應(yīng)于這些形參的實參將會執(zhí)行正常的類型檢查,形參后面的逗號是可以選擇的。
  • void foo(...);? 省略符形參所對應(yīng)的實參不需要類型的檢查

6.3 返回類型和return語句

  • return語句終止當(dāng)前正在執(zhí)行的函數(shù)并將控制權(quán)返回到該函數(shù)被調(diào)用的地方。
  • 兩種形式:return ; 和 return expression;

6.3.1 無返回值函數(shù)

  • 沒有返回值的return語句只能用于返回類型是void的函數(shù)中。返回void的函數(shù)不需要非得有return語句,因為在該類函數(shù)的最后一句會隱式執(zhí)行return。
  • 通常情況下,void函數(shù)如果想在他的中間位置退出,可以使用return語句,這個用法類似break。
  • 例如:寫一個swap函數(shù),使其在參與交換的數(shù)值相等的時候,什么也不做,直接退出
void swap(int &v1,int &v2){if(v1 == v2){return ;}int tmp = v2;v2 = v1;v1 = tmp; } int main(){int s1 = 1;int s2 = 1;swap(s1,s2);return 0; }
  • 一個返回類型是void的函數(shù)也可以使用return的第二種形式,不過此時return語句的expression必須是另外一個void的函數(shù)。強行令void函數(shù)返回其他類型的表達(dá)式將產(chǎn)生編譯錯誤。

6.3.2 有返回值的函數(shù)

  • 只要函數(shù)的返回類型不是void,那么函數(shù)內(nèi)部的每一個return語句必須返回一個數(shù)值。
  • return的返回類型必須和函數(shù)的返回類型相互一致,或者隱式轉(zhuǎn)換成函數(shù)的返回類型
  • 在含有return語句的循環(huán)后面的應(yīng)該也有一條return語句,如果沒有的話該程序是錯誤的,很多編譯器都無法發(fā)現(xiàn)這個錯誤

值是如何返回的

  • 數(shù)值的返回和初始化一個變量或者形參的方式是完全一樣的,返回的數(shù)值用于初始化調(diào)用點的一個臨時變量,該臨時變量就是函數(shù)調(diào)用的結(jié)果。
  • 必須注意函數(shù)返回局部變量時的初始化規(guī)則。
  • 例子:給定計數(shù)值、單詞和結(jié)束符之后,判斷計數(shù)值是否大于1,是的話,返回單詞的復(fù)數(shù);否的話,返回單詞的原型
//如果str的數(shù)值大于1,返回word的復(fù)數(shù)的形式 string make_plural(size_t ctr, const string &word, char ending){return (ctr > 1) ? word + ending : word; } int main(){string s1 = "Hello Hello string world";cout << make_plural(2,s1,'s') ;return 0; }
  • 這個函數(shù)返回的類型是string,意味著返回值將會被拷貝到調(diào)用點。因此,該函數(shù)將會返回一個word的副本或者一個未命名的臨時的string對象,該對象是word和string的和。
  • 同其他引用的類型一樣,如果函數(shù)返回引用,則該引用僅僅是它所引用對象的一個別名。
  • 例子:函數(shù)返回兩個string中形參較短的那個并且返回其引用,其中形參和返回類型都是const string的引用,不管是調(diào)用函數(shù)還是返回的結(jié)果都不會真正的拷貝string對象。
//跳出兩個string對象中較短的那個,并且返回其引用 const string &shorterString(const string &s1,const string &s2){return s1.size() < s2.size() ? s1 : s2; } int main(){string s1 = "Hello Hello string world";string s2 = "Hello Hello";cout << shorterString(s1,s2) ;return 0; }

不要返回局部對象的引用或者指針

  • 函數(shù)執(zhí)行完畢之后,會釋放掉占用的存儲空間,因此函數(shù)的終止意味著將局部變量的引用指向不再有效的內(nèi)存區(qū)域。
  • 錯誤的原因在于試圖訪問未定義的變量
  • 返回局部對象的指針也是錯誤的,函數(shù)完成,局部對象釋放,指針將會指向一個不再可用的內(nèi)存空間
//嚴(yán)重錯誤:這個函數(shù)試圖返回局部對象的引用 cosnt string &manip(){string ret;//通過某種凡是改變一下retif(!ret.empty()){return ret;//錯誤,返回的是一個對于局部變量的引用}else{return "Empty!";//錯誤:"Empty是一個局部臨時的變量"} }

返回類類型函數(shù)和調(diào)用運算符

  • 調(diào)用運算符存在優(yōu)先級和結(jié)合律,其優(yōu)先級和點運算符號和箭頭運算符號等同,并且也符合左結(jié)合律。
  • 如果函數(shù)返回的指針、引用或者類對象,就可以通過函數(shù)調(diào)用的結(jié)果來訪問結(jié)果對象的成員。
  • 例如,通過如下的形式得到較短的string對象的長度(這個例子相當(dāng)于調(diào)用對象的子函數(shù))
//跳出兩個string對象中較短的那個,并且返回其引用 const string &shorterString(const string &s1,const string &s2){return s1.size() < s2.size() ? s1 : s2; }int main(){string s1 = "Hello Hello string world";string s2 = "Hello Hello"; // cout << shorterString(s1,s2) ;auto sz = shorterString(s1,s2).size();cout << sz << endl;return 0; }

引用返回左值

  • 函數(shù)的返回類型決定了函數(shù)的調(diào)用是否是左值。
  • 返回引用的函數(shù)得到左值,其他類型的函數(shù)返回右值。可以像使用其他左值的使用方式一樣來返回引用的函數(shù)的調(diào)用,特別的是,可以為返回類型是非常量的引用的函數(shù)的結(jié)果進行賦值。
char &get_val(string &str,string::size_type ix){return str[ix]; } int main(){string a("a Value"); //輸出a valuecout << a << endl;get_val(a,0) = 'A';//將s[0]的數(shù)值改為Acout << a << endl; //將輸出A valuereturn 0; }
  • 返回讀的值是引用,因此調(diào)用是一個左值,和其他的左值一樣他也能出現(xiàn)在賦值運算符號的左側(cè)
  • 如果返回的類型是對于常量的引用,不能給調(diào)用的結(jié)果賦值

列表初始化返回值

  • 函數(shù)可以返回花括號內(nèi)包圍的熟知的列表。類似于其他返回的結(jié)果,此處的列表也用來對于對于表示函數(shù)返回的臨時量進行初始化。如果列表為空,臨時量執(zhí)行數(shù)值初始化,否則返回的數(shù)值由函數(shù)的返回的類型決定。

主函數(shù)main的返回數(shù)值

  • 函數(shù)的返回類型不是void,必須返回一個數(shù)值,但是mian函數(shù)例外,允許main函數(shù)沒有return語句直接結(jié)束。如果控制到達(dá)了main函數(shù)的結(jié)尾而且沒有return語句,編譯器會隱式插入一個返回為0的return語句。
  • mian函數(shù)的返回數(shù)值可以看做是狀態(tài)的指示器。返回0表示執(zhí)行成功,其他數(shù)值表示返回失敗,其中非0的數(shù)值由具體機器而定。為了使得返回的類型和機器無關(guān),可以引入兩個預(yù)處理的變量,分別用來表示成功和失敗。
if(1){return EXIT_SUCCESS;}else{return EXIT_FAILURE;}
  • 其中EXIT_SUCCESS和EXIT_FAILURE定義在頭文件cstdlib頭文件中
  • 因為上面兩個變量屬于預(yù)處理變量,因此既不需要在前面加上std::也不能在using聲明中出現(xiàn)。

遞歸

  • 如果一個函數(shù)調(diào)用了它自身,無論是直接還是間接調(diào)用都稱該函數(shù)是遞歸函數(shù)。
  • 例子:使用遞歸函數(shù)實現(xiàn)求階乘的功能
int factorial(int val){if(val > 1 ){return factorial(val - 1) * val;return 1;} } int main(){cout << factorial(5) << endl;return 0; }
  • 注意事項,在使用遞歸函數(shù)的時候,一定會包含一支路徑是不包含遞歸調(diào)用的,否則函數(shù)將會永遠(yuǎn)的執(zhí)行下去。

6.3.3 返回數(shù)組的指針

  • 因為數(shù)組不能被拷貝,因此函數(shù)不會返回數(shù)組。但是,函數(shù)可以返回數(shù)組的指針或者引用,其中最為有效的方法是使用類型別名的方式。
typedef int arrT[10];//arrT是一個類型的別名,它表示的類型是含有10個整數(shù)的數(shù)組using arrT = int[10];//arrT的等價聲明arrT* func(int i);//func返回一個指向含有10個整數(shù)的數(shù)組的指針

聲明一個返回數(shù)組指針的函數(shù)

  • 要想在聲明func時不使用類型別名,必須牢記被定義的名字后面的數(shù)組的維度
int arr[10]; //arr是一個含有10個整數(shù)的數(shù)組int *p1[10]; //p1是一個含有10個指針的數(shù)組int (*p2)[10] = &arr; //p2是一個指針,指向含有10個整數(shù)的數(shù)組
  • Type (*function(parameter_list))[dimension]
  • 其中Type表示元素的類型,dimension表示數(shù)組的大小,(*function(parameter_list))兩邊的括號必須存在,就像上面對于p2的定義一樣,如果沒有這對括號,函數(shù)的返回類型是指針的數(shù)組。

例子

  • int (*func(int i))[10];
  • func(int i)表示調(diào)用func函數(shù)的時候需要一個int類型的實參
  • (*func(int i))[10] 表示解引用func的調(diào)用將得到一個大小是10的數(shù)組
  • int (*func(int i))[10]表示數(shù)組中的元素類型是int類型

使用尾置返回類型

  • 可以使用尾置返回類型來替代上面提到的func聲明的方法。
  • 任何函數(shù)都可以使用尾置返回,即使是返回類型比較復(fù)雜的函數(shù)也仍然可以使用,比如返回類型是數(shù)組的指針或者數(shù)組的引用。
  • 尾置返回類型跟在形參列表的后面,并且以一個->符號開頭。為了表示函數(shù)真正的返回類型跟在形參列表之后,在本應(yīng)該出現(xiàn)返回類型的地方放置一個auto。
//func接受一個int類型的實參,返回一個指針,這個指針指向含有10個整數(shù)的數(shù)組 auto func(int i) -> int(*)[10];
  • 把函數(shù)的返回值類型放在了形參列表的后面,所以func函數(shù)返回的是一個指針,并且這個指針指向了含有10個整數(shù)的數(shù)組

使用decltype(這一部分有問題)

  • decltype關(guān)鍵字一般用于知道函數(shù)返回的指針指向哪個數(shù)組的時候進行對于返回類型的聲明。
  • 例子:函數(shù)返回一個指針,該指針可以根據(jù)參數(shù)i的不同指向兩個已知數(shù)組中的一個
int odd[] = {1,3,5,7,9};int even[] = {0,2,4,6,8};//返回一個指針,這個指針指向含有5個整數(shù)的數(shù)組decltype (odd) *arrPtr (int i){return (i % 2) ? &odd :&even;}
  • 程序無法執(zhí)行
  • decltype并不負(fù)責(zé)把數(shù)組類型轉(zhuǎn)化成對應(yīng)的指針,所以decltype的結(jié)果是一個數(shù)組,要想表示attPtr返回指針還必須在函數(shù)生命的時候加上一個*號。

總結(jié)

以上是生活随笔為你收集整理的C++ primer第六章函数的学习的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。