2020 我的C++的学习之路 第八章函数
以C++ Primer Plus為參考書籍,自身歸納知識點,加深記憶。
第八章 函數探幽
- 1 內聯函數
- 2 引用變量
- 2.1 創建引用變量
- 2.2 引用與函數參數
- 2.3 引用與結構
- 何時使用引用參數
- 默認參數
- 函數重載
- 函數模板
- 顯式具體化
- 實例化與具體化
- 編譯器選擇哪個函數版本
- 完全匹配和最佳匹配
- 部分排序規則
- 關鍵詞decltype(C++11)
- 后置返回類型(C++11)
1 內聯函數
常規的函數調用使程序跳到一個地址并在結束時返回,對于內聯函數而言,程序無需跳轉即可執行,因此內聯函數運行速度比常規函數稍快,但代價是占用更多內存。倘若程序在10個不同的地方調用同一個內聯函數,該程序則包含該函數代碼的10個副本。
要使用內聯函數,必須采取下述措施之一:
① 函數聲明前加關鍵字inline
②函數定義前加關鍵字inline
通常的做法是省略原型,將函數頭以及函數代碼放在本應提供原型的地方。
內聯函數與宏
C語言使用預處理器語句#define提供宏
#define SQUARE(X) X*X
本身并不是通過傳遞參數實現,而是通過文本替換實現:
a = SQUARE(5.0); 其實就是a=5.0 * 5.0;
b=SQUARE(4.5+1.5)::::其實就是b=4.5+1.5 * 4.5+1.5
SQUARE(c++):::::(c++) * (c++)不同編譯器給出的結果還不一樣
內聯函數通過傳遞參數實現,因此在square(c++)中,傳遞c給square()函數然后再c++,在編譯器上沒有沖突存在。
2 引用變量
引用變量是C++新增的復合類型,引用是已定義變量的別名,通過將引用變量做參數,函數使用原始數據,而不是副本。
2.1 創建引用變量
int count; int &cnt = count;//int&指的是指向int的引用,count與cnt指向相同的值和內存單元上述例子中,倘若對cnt做遞增運算,那么count也會做相應的變化。
int count; int &cnt; cnt = count;//引用不同于指針,必須在聲明時就用初始化該引用。2.2 引用與函數參數
#include<iostream> void swapr(int &a,int &b); void swapp(int *a,int *b); void swapv(int a,int b);int main() {using namespace std;int wallet1 = 300;int wallet2 = 350;swapr(wallet1,wallet2);cout<<wallet1<<" "<<wallet2<<endl; // 350 300swapp(&wallet1,&wallet2);cout<<wallet1<<" "<<wallet2<<endl; // 300 350swapv(wallet1,wallet2);cout<<wallet1<<" "<<wallet2<<endl;// 300 350return 0; } void swapr(int &a,int &b) {int temp;temp = a;a = b;b = temp; } void swapp(int *a,int *b) {int p;p = *a;*a = *b;*b = p;} void swapv(int a,int b) {int temp;temp = a;a = b;b = temp; }在swapr()中變量a,b是wallet1,wallet2的別名,所以兩者交換也將wallet1,2的值進行了交換,但swapv()中的a和b是復制了wallet1,2的新變量,將它倆交換并不影響main()中的變量的值,因此需要利用指針指出wallet1,2內存所在的地址方可進行交換。
2.3 引用與結構
引用非常適合用于結構和類,假設有如下結構定義:
struct free {string name;int made;int attempts;float percent; }則可以這樣編寫函數原型,在函數中將指向該結構的引用作為參數:
void set_pc(free& ft); void display(const free& ft);//倘若不想修改傳入的結構,關鍵詞const #include <iostream> #include <string> struct free_throws {std::string name;int made;int attempts;float percent; };void display(const free_throws & ft); void set_pc(free_throws & ft); free_throws & accumulate(free_throws &target, const free_throws &source);int main() {free_throws one = {"Ifelsa Branch", 13, 14};free_throws two = {"Andor Knott", 10, 16};free_throws three = {"Minnie Max", 7, 9};free_throws four = {"Whily Looper", 5, 9};free_throws five = {"Long Long", 6, 14};free_throws team = {"Throwgoods", 0, 0};free_throws dup;set_pc(one);display(one);accumulate(team, one);display(team); // use return value as argumentdisplay(accumulate(team, two));accumulate(accumulate(team, three), four);display(team); // use return value in assignmentdup = accumulate(team,five);std::cout << "Displaying team:\n";display(team);std::cout << "Displaying dup after assignment:\n";display(dup);set_pc(four); // ill-advised assignmentaccumulate(dup,five) = four;//five的數據給到dup中,再用four覆蓋dupstd::cout << "Displaying dup after ill-advised assignment:\n";display(dup);// std::cin.get();return 0; }void display(const free_throws & ft) {using std::cout;cout << "Name: " << ft.name << '\n';cout << " Made: " << ft.made << '\t';cout << "Attempts: " << ft.attempts << '\t';cout << "Percent: " << ft.percent << '\n'; } void set_pc(free_throws & ft) {if (ft.attempts != 0)ft.percent = 100.0f *float(ft.made)/float(ft.attempts);elseft.percent = 0; }free_throws & accumulate(free_throws & target, const free_throws & source) {target.attempts += source.attempts;target.made += source.made;set_pc(target);return target; }何時使用引用參數
對于使用傳遞的值不做修改的函數:
①數據對象很小,如內置數據類型或小型結構,則按值傳遞;
②數據對象是數組,則使用指針,并聲明為const
③數據對象是大型結構,則使用const指針或者const引用,節省復制結構所需的時間和空間
④數據對象是類對象,使用const引用
對于修改調用函數中數據的函數
①數據對象是內置數據類型,則使用指針
②數據對象是數組,則只能使用指針
③數據對象是結構,則使用引用或指針
④數據對象是類對象,使用引用
默認參數
默認參數指的是當函數調用中省略了實參時自動使用的一個值。
設置默認值必須通過函數原型:
對于帶參數列表的函數,必須從右向左添加默認值
int harp(int n, int m=2, int j=3);//可用int hard(int n,int m =3, int j);//不可用int saiji(int n =5, int m =1, int j =9);//可用實參從左到右的順序依次賦給相應的形參,而不能跳過任何參數。只有在函數原型中指明了默認值,函數定義與沒有默認參數時完全相同。
函數重載
默認參數能夠使用不同數目的參數調用同一個函數,而函數多態(函數重載)能夠使用多個同名的函數,多態是指有多種形式,重載是指有多個重名的函數,兩個術語是一回事,但通常使用函數重載。
函數重載的關鍵是函數的參數列表,也成為函數特征標,如果兩個函數的參數數目,排列順序都相同,則它們的特征標相同,變量名無關緊要。函數重載的條件就是特征標不同。
上述函數的特征標均不相同,并且返回類型也可以不同,因此可認為是函數重載。
double cube(double x); double cube(double &x);編譯器在檢查函數特征標時,把類型的引用與類型本身視為同一個特征標。
函數模板
函數模板是通用的函數描述,使用泛型來定義函數,其中的泛型可用具體的類型(int,double等)替換,函數模板允許以任意類型的方式定義函數,如以下模板所示:
template<class T>//第一行template必需,class與typename可替換,必須使用尖括號 void swap(T& a,T& b)//類型名可以任意選擇,但命名規則要遵守 {T temp;temp = a;a = b;b = temp; } // funtemp.cpp -- using a function template #include <iostream> // function template prototype template <typename T> // or class T void Swap(T &a, T &b);int main() {using namespace std;int i = 10;int j = 20;cout << "i, j = " << i << ", " << j << ".\n";cout << "Using compiler-generated int swapper:\n";Swap(i,j); // generates void Swap(int &, int &)cout << "Now i, j = " << i << ", " << j << ".\n";double x = 24.5;double y = 81.7;cout << "x, y = " << x << ", " << y << ".\n";cout << "Using compiler-generated double swapper:\n";Swap(x,y); // generates void Swap(double &, double &)cout << "Now x, y = " << x << ", " << y << ".\n";// cin.get();return 0; }// function template definition template <typename T> // or class T void Swap(T &a, T &b) {T temp; // temp a variable of type Ttemp = a;a = b;b = temp; }顯式具體化
C++98標準:
①對于給定的函數名,可以有非模板函數、模板函數和顯式具體化函數及其重載版本
②顯示具體化的原型和定義應以template<>打頭,并通過原型指出類型
③具體化優先于常規模板,而非模板函數優于具體化和常規模板
實例化與具體化
代碼中包含函數模板本身并不會生成函數定義, 只是一個用于生成函數定義的方案,當編譯器使用模板為特定類型生成函數定義時,得到的便是函數實例,例如上述swap(i,j)便是模板函數swap()的一個實例,這種實例化的方式成為隱式實例化。
顯式實例化的語法是聲明所需的種類,用<>表示類型,并在聲明前加上關鍵字template:
顯式具體化,顯式實例化,隱式實例化統稱為具體化,相同之處在于表示的都是使用具體類型的函數定義,而不是通用描述。前綴template與前綴template<>分別代表顯示實例化和顯式具體化
編譯器選擇哪個函數版本
完全匹配和最佳匹配
①指向非const數據的指針和引用優先與非const指針和引用參數匹配
②const和非const之間的區別只適用于指針和引用指向的數據
③完全匹配優于另一個的情況還有一個是非模板函數,而另一個不是,此時非模板函數優于模板函數(包括顯式具體化)
④如果兩者都是模板函數,那么較具體的模板函數優先。
部分排序規則
重載解析尋找最匹配的函數:
如果只存在一個這樣的函數,則選擇它;
如果存在多個這樣的函數,但其中只有一個是非模板函數,則選擇非模板函數;
如果存在多個合適的函數,并且都為模板函數,但只有一個比其他更具體,則選擇該函數;
如果有多個同樣合適的非模板或模板函數,但沒有一個更具體的,則會報錯;
如果不存在匹配的,也會報錯
關鍵詞decltype(C++11)
decltype (x+y)xpy;//xpy的類型為x+y的類型 xpy = x+y;//等價于decltype(x+y)xpy=(x+y)decltype (expression) var
decltype的確定類型:
①如果expression是一個沒有用括號括起的標識符,則var與該標識符的類型相同,包括const等限定符;
②如果expression是一個函數調用,則var與函數返回類型相同;
long indeed(int); decltype(indeed(3))m;//m是long類型,并且函數沒有實際調用③如果expression是一個左值,則var指向其類型的引用,一種顯而易見的情況就是expression是用括號括起的標識符;
double xx = 4.4; decltype((xx))r2 = xx;//r2是一個double的引用 decltype(xx)W = xx;//w是一個double④如果前面條件都不滿足,那么var與expression類型相同
int j=3; int &k =j; int &n =j; decltype(j+6)x;//x為int decltype(100L)y;//y為long decltype(k+n)z;//z為int,k,n為引用,但兩者相加得到的是int后置返回類型(C++11)
對于下面的原型:
double h(int,float);使用新增的語法可編寫成:
auto h(int,float) -> double;//auto是一個占位符,表示后置返回類型提供的類型總結
以上是生活随笔為你收集整理的2020 我的C++的学习之路 第八章函数的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 2020 我的C++学习之路 C++Pr
- 下一篇: 2020 我的C++学习之路 C++Pr