C++另一種編程思想稱為泛型編程,主要利用的技術是模板
C++提供兩種模板機制:函數模板和類模板
C++提供了模板(template)編程的概念。所謂模板,實際上是建立一個通用函數或類,
其類內部的類型和函數的形參類型不具體指定, 用一個虛擬的類型來代表。
這種通用的方式稱為模板。模板是泛型編程的基礎,泛型編程即以一種獨立于任何特定類型的方式編寫代碼。
即:我們提供一個抽象的函數或類,并不具體指定其中數據的類型,而是某個虛擬類型代替。只提供基本的功能。其具體的數據類型,只在其被調用時視具體情況實例化。
函數模板
舉個例子
#include <iostream>
#include <stdio.h>using namespace std;template <typename T1,typename T2> //模板函數聲明與定義
T2 test(T1 tmp, T2 tmp1) {T2 tmp2 = tmp + tmp1;return tmp2;
}int main(void) {cout << "test(10, 5)=" << test(10, 5) << endl; //調用模板函數,模板函數通過傳入的參數自動推導未實例化的類型cout << "test(5,'A')=" << test(5,'A') << endl;cout << "test(10.5, 5.5) =" << test(10.5, 5.5) << endl;system("pause");return 0;
}
函數模板的聲明通過關鍵字template與typename 實現。其中,template告知編譯器這是函數模板的聲明,typename用來聲明虛擬類型。比如你要聲明一個模板函數,里面需要兩個不同的變量,那么你就需要通過typename聲明兩個不同的虛擬類型T1,T2。
聲明好后,你就可以在函數定義中使用虛擬類型來定義變量,但是要注意,用同一個虛擬類型定義的變量就只能是同種類型,比如用T1定義的變量只能是同種變量,可以是int,也可以是char。這取決于其實例化時被實例化為哪種類型。
C++函數模板注意事項
注意事項:
1、自動類型推導,必須推導出一致的數據類型T,才可以使用
2、模板必須要確定出T的數據類型,才可以使用
using namespace std;
template<class T>
void mySwap(T& a, T& b)
{T temp = a;a = b;b = temp;
}void test01()
{int a = 10;int b = 20;char c = 'c';//1、自動類型推導,必須推導出一致的數據類型T,才可以使用mySwap(a, b);//mySwap(a, c);推導不出一致的T類型cout << "a = " << a << endl;cout << "b = " << b << endl;
}template<class T>
void func()
{cout << "func()的調用" << endl;
}void test02()
{//2、模板必須要確定出T的數據類型,才可以使用func<int>();
}int main() {test01();test02();return 0;
}
模板函數的調用
1)顯式類型調用
可以顯式的調用模板函數,即在調用時人為地指明虛擬類型的具體類型。
#include <iostream>
#include <stdio.h>using namespace std;template <typename T1,typename T2> //模板函數聲明與定義
T2 test(T1 tmp, T2 tmp1) {T2 tmp2 = tmp + tmp1;return tmp2;
}int main(void) {cout << "test(5,'A')=" << test<int,char>(5, 'A') << endl; //<int,char>顯式的指明模板的類型system("pause");return 0;
}
2)自動推導
即不指明具體的數據類型,而讓編譯器根據傳入的數據自動推導出數據類型。
#include <iostream>
#include <stdio.h>using namespace std;template <typename T1,typename T2> //模板函數聲明與定義
T2 test(T1 tmp, T2 tmp1) {T2 tmp2 = tmp + tmp1;return tmp2;
}int main(void) {cout << "test(5,'A')=" << test(5, 'A') << endl; //自動推導數據類型system("pause");return 0;
}
模板函數與函數重載
熟悉函數重載的人應該會好奇,如果既有模板函數又有同名的普通函數,而且參數列表的參數個數是一樣的,那么在主函數中調用同名函數,編譯器具體會調用哪一個呢?
下面看一個例子:
#include <iostream>
#include <stdio.h>using namespace std;template <typename T1,typename T2> //模板函數聲明與定義
T1 test(T1 tmp, T2 tmp1) {cout << "調用模板函數!" << endl;return (tmp + tmp1);
}int test(int tmp, int tmp1) { //重載的普通函數cout << "調用普通函數!" << endl;return 0;
}int main(void) {char tmp = 'c';int tmp1 = 0;int a = 5;cout << "test(5,'c')=" << test(a, tmp) << endl; cout << "test(5,0)=" << test(a, tmp1) << endl;system("pause");return 0;
}
普通函數的兩個參數都是int型,在第一次調用test時第二個參數使用的是char型,調用的是模板函數,第二次使用的是int型,調用的是普通函數。
這是為什么呢?理論上來說,模板函數兩個都能匹配,使用。而普通函數也能匹配這兩次調用的參數(在C語言中,char型變量是可以作為int型參數使用的)。
這是因為模板函數可以自動推導類型,在第一次調用中,兩個類型分別被推導為int型與char型。而普通函數是兩個int型,雖然也能使用傳入的參數,但模板函數明顯能更好的匹配參數列表。
也就是說,如果模板函數實例化后的類型能更好的匹配參數列表的話就使用模板函數。
那么當這兩個函數都能完全匹配參數列表的時候呢?通過第二次test的調用結果不難發現,這時候,編譯器會調用普通函數。
如果一定要使用模板函數,可以使用<>顯式的指定使用模板函數。看下面的例子。
#include <iostream>
#include <stdio.h>using namespace std;template <typename T1,typename T2> //模板函數聲明與定義
T1 test(T1 tmp, T2 tmp1) {cout << "調用模板函數!" << endl;return (tmp + tmp1);
}int test(int tmp, int tmp1) { //重載的普通函數cout << "調用普通函數!" << endl;return 0;
}int main(void) {char tmp = 'c';int tmp1 = 0;int a = 5;cout << "test(5,'A')=" << test(a, tmp) << endl; cout << "test<>(5,0)=" << test<>(a, tmp1) << endl; //使用<>顯式的調用模板函數system("pause");return 0;
}
類模板
類模板是為了減少重復工作量而出現的一種進制,當一個類的功能類似只是類型不同時,一個通用的類模板可以根據使用者的需要而生成具體類型的類,從而減少功能重復的代碼。
類模板作用:建立一個通用類,類中的成員 數據類型可以不具體制定,用一個虛擬的類型來代表
解釋:
template–聲明創建模板
typename–表明其后面的符號是一種數據類型,可以用class代替
T–通用的數據類型,名稱可以替換,通常為大寫字母
在類內部定義與聲明
#include <iostream>
#include <stdio.h>using namespace std;template <typename T>
class DEMO {public:DEMO(T data) {this->data = data;}~DEMO() {}int GetData() {return this->data;}private:T data;
};template <typename T,typename T1>
class DEMO1 {public:DEMO1() {}~DEMO1();private:T data;T1 ch;
};int main(void) {DEMO<int> demo(5); //顯示的指定類型為intDEMO1<int, char> demo1(); //顯示的指定類型分別為int,charcout << "data=" << demo.GetData() << endl;system("pause");return 0;
}
類模板的定義與使用使用的是和模板函數一樣的關鍵字。即先聲明template,在使用typename聲明虛擬類型。
與模板函數不同的是,類模板不能被自動推導出類型,只能顯示的指定具體的類型。如上面代碼中的 DEMO< int > demo(5),該模板類被顯示的指定為int型。
在類外部定義成員函數
在類內部聲明成員函數,在類外部定義成員函數時,只要成員函數參數列表中出現了類限定域說明,模板類作為返回類型,模板類作為參數類型,那么就要在成員函數之前聲明 template <類型形式參數表>,并且在模板類后加上虛擬參數列表。
#include <iostream>
#include <stdio.h>using namespace std;template <typename T>
class DEMO {public:DEMO(T data);~DEMO();DEMO operator+(int sum);int PrintData(DEMO& demo);private:T data;
};template<typename T> //出現了類限定域說明
DEMO<T>::DEMO(T data)
{this->data = data;
}template<typename T> //出現了類限定域說明
DEMO<T>::~DEMO()
{
}template<typename T> //出現了作為返回值類型的模板類類型
DEMO<T> DEMO<T>::operator+(int sum)
{return *this;
}template<typename T> //出現了作為參數類型的模板類類型
int DEMO<T>::PrintData(DEMO<T>& demo)
{cout << "data=" << demo.data << endl;return 0;
}int main(void) {DEMO<int> demo(5), demo1(15);demo.PrintData(demo1);demo + 5;system("pause");return 0;
}
DEMO< T >中的< T >是虛擬參數列表。
總結來說,只要看到了模板類的類名關鍵字出現在成員函數參數列表中,就要在成員函數之前聲明 template <類型形式參數表>,并且在模板類類名后加上虛擬參數列表。
模板類的繼承
分為三種情況。一:子類是模板類,父類是普通類;二:父類是模板類,子類是普通類;三:父類與子類都是模板類。其中第一種情況與兩個普通類的繼承是一樣的。就不說了。
1)父類是模板類,子類是普通類:
#include <iostream>
#include <stdio.h>using namespace std;template <typename T>
class FATHER {public:FATHER(T data) {this->data = data;}~FATHER() {}private:T data;
};class SON:public FATHER<int> { //顯示的指明父類的具體類型public:SON(int data):FATHER<int>(data) {this->data = data;}~SON() {}int GetData() {return data;}private:int data;
};int main(void) {SON son(15);cout << "data=" << son.GetData() << endl;system("pause");return 0;
}
子類是一般類,父類是模板類,繼承時必須在子類里實例化父類的類型參數。其實這很好理解,因為在子類對象構造之前,會先調用父類的構造函數,父類為模板類,要想實例化,需要有指定的類型。
2)父類與子類都是模板類:
#include <iostream>
#include <stdio.h>using namespace std;template <typename T>
class FATHER {public:FATHER(T data) {this->data = data;}~FATHER() {}private:T data;
};template <typename T1>
class SON:public FATHER<T1> { //使用子類的模板類型傳遞到父類中,也可以使用具體的類型public:SON(int data):FATHER<int>(data) {this->data = data;}~SON() {}int GetData() {return data;}private:T1 data;
};int main(void) {SON<int> son(15);cout << "data=" << son.GetData() << endl;system("pause");return 0;
}
當子類與父類都是模板類時,繼承時也必須在子類里實例化父類的類型參數,值得注意的是,此時實例化的類型參數可以使用子類的模板類型。即讓父類與子類在實例化后擁有一樣的具體類型。當然也可以使用其它的具體類型。