C++複習筆記(六)之函式指標和函式模板、類模板
一、函式指標
函式指標在C語言中的作用類似於c++中的多型,都是可以實現框架的搭建,程式碼的相容性高。
函式三要素:名稱、引數、返回值
C語言可以通過typedef為函式型別重新命名,語法 typedef 返回值型別(型別名稱)(引數列表);如下程式碼所示:
#include<iostream> using namespace std; int func(int a, int b) { return a + b; } int main() { //函式指標型別定義的第一種方式 : 宣告一個 函式型別 typedef int(Func1)(int, int); Func1 *hello1= func; cout << hello1(7, 8) << endl; //函式指標型別定義的第二種方式 : 宣告一個函式指標型別 typedef int(*Func2)(int, int); Func2 hello2 = func; cout << hello2(7, 8) << endl; //函式指標定義的第三種方式 : 定義一個指向函式入口地址的函式指標 int(*Func3)(int a, int b); Func3 = func; cout << Func3(7, 8) << endl; system("pause"); return 0; }
二、函式指標的本質
對於編譯器來講,資料型別的本質就是,不同大小的記憶體塊的別名,同樣函式也是一種資料型別,那麼函式名就是函式的入口地址。函式指標就是向編譯器申請類一個型別函式大小的記憶體,這個塊記憶體存放的是一個函式的入口地址。
我們看函式指標定義的前面兩種方式,我們通過typedef對函式型別進行了重新命名: typedef int(Func1)(int, int) 就是告訴編譯器我現在自定義了一種資料型別 Func1,這個資料型別需要傳入兩個int型的引數,返回一個int型引數(此時編譯器並沒有對該資料型別進行世實際的記憶體分配)。 當 Func1 *hello1= func 這個時候便器器對hello這個指標分配了一個具有Func1特性與大小的記憶體,並讓這個塊記憶體的初始值為func這個函式的入口地址。
三、函式指標做函式引數
當函式指標做函式引數時、傳遞給一個被呼叫函式,被呼叫函式就可以通過這個指標呼叫外部的函式,這樣就形成了回撥。
回撥函式的本質:提前做了一個協議約定(把函式的引數,返回值提前約定)所有被呼叫的函式只要符合這個約定,無論函式內部如何實現都是可以的(實現瞭解耦合)
例項演示:
需求:我們現在要求求出兩個數的加減乘三種運算,我們可以先通過約定函式模型,然後進行分工實現,一個人A實現上層的展示與呼叫,一個人B實現三種運算。
A是實現的部分
#include<iostream> using namespace std; typedef int(*Func)(int, int); //通過函式指標提前約定呼叫規範。 void player(Func p) { cout << p(10, 8) << endl; } int main() { system("pause"); return 0; }
B實現的部分:
int func1(int a, int b)
{
return a + b;
}
int func2(int a, int b)
{
return a * b;
}
int func3(int a, int b)
{
return a - b;
}
B需要給A一個myfun.h標頭檔案,裡面放了三個函式的宣告,和一個func.cpp檔案裡面放了三個函式的實現。B只需要這兩個檔案給A,A就可以在實際中將myfun.h標頭檔案聲明後對,裡面的函式進行呼叫,完全不需要考慮B如何是實現的。
最終A的程式碼:
#include<iostream>
using namespace std;
#include"myfunc.h"
typedef int(*Func)(int, int); //通過函式指標提前約定呼叫規範。
void player(Func p)
{
cout << p(10, 8) << endl;
}
int main()
{
player(func1);
player(func2);
player(func3);
system("pause");
return 0;
}
這樣就是簡單實現了程式碼的解耦合.
四、函式模板
所謂函式模板,實際上是建立一個通用函式,其函式型別和形參型別不具體指定,用一個虛擬的型別來代表。這個通用函式就稱為函式模板。
函式模板定義語法:
函式模板呼叫:
模板函式名<引數資料型別>(引數列表) //顯示引數型別呼叫(強制呼叫)
模板函式名<>(引數列表) //顯示呼叫(強制呼叫)
模板函式名(引數列表) //自動資料型別推導(非強制型別呼叫)
函式過載與模板函式
模板函式和普通函式一起時的呼叫規則:
1 、函式模板可以向普通函式一樣被過載
2、C++編譯器優先考慮普通函式
3、如果函式木板可以產生一個更好的匹配(普通函式會發生引數型別的強制轉換),那麼選擇模板
3、可以通過空模板實參列表( 模板函式名<>(引數列表) )的語法限定編譯器只能通過模板匹配。
4、函式模板不允許自動型別轉換(引數型別轉換),普通函式能夠發生自動型別轉換。
函式模板機制結論
編譯器並不是把函式模板處理成能夠處理任意類的函式,編譯器是從函式模板通過具體型別產生不同的函式,編譯器會對函式模板進行兩次編譯:(1)在宣告的地方對模板程式碼本身進行編譯;(2) 在呼叫的地方對替換引數後的程式碼進行編譯
五、類模板
1、模板類中如果使用了建構函式,則遵守以前的類的建構函式的呼叫規則
2、當子類從模板類繼承時,需要指定模板類的資料型別。
3、類模板做函式引數時,需要我們將模板類例項化,因為編譯器會對函式進行記憶體分配(包括引數等),因此需要知道具體的資料型別,只有資料型別固定下來,才可以分配記憶體。
類模板和函式模板的機制一樣都是兩次編譯,所以不同的模板類接收不同的引數列表之後,生成的類是完全獨立的,擁有獨立的內記憶體空間。
有static 關鍵字定義的成員變數的模板類,每個類模板在確定引數後都會有獨自的static關鍵字的儲存單位,之間相互不影響,但是static關鍵自定義的成員變數的屬性在同一個類中的屬性不變。
比如有一個類模板
template <typename T>
class A
{
public:
static int a;
private:
T data;
}
int A::a = 0;
我們使用它例項化引數後生成了兩個模板類 A<int> 、A<char> 並建立了 兩個不同型別的物件a1,c1;
A<int> a1;
A<char> c1;
所有的A<int> 模板類建立的物件共同使用同一塊記憶體存放static關鍵字定義的成員變數;所有的A<char> 模板類建立的物件共同使用同一塊記憶體存放static關鍵字定義的成員變數,兩個互不相同。
六、類模板的好處
1、它是型別無關的,具有高度的可複用性
2、它在編譯時而不是執行時檢查資料型別,保證了型別安全。
3、與平臺無關,可移植性。
函式模板與類模板的區別:函式模板的例項化是由編譯程式在處理函式呼叫時自動完成的;而類模板的例項化必須由程式設計師在程式中顯式地指定。