泛型編程與面向對象編程都依賴於某種形式的多態性,從而更好的實現代碼的重用。
面向對象的多態是運行時應用存在繼承關係的類,忽略基類和派生類之間類型的差異,只需要使用基類的引用或者指針,基類類型或派生類類型的對象就可以使用相同的代碼。
泛型編程的多態是所編寫的函數或類能夠跨越編譯時不相關的數據類型。一個類或者函數可以操縱多種類型的對象。標準庫中的容器、迭代器和算法都使用了泛型編程。比如
vector<int>
和vector<string>
。
1、函數模版
1.1、函數模版定義和使用
int compare(const string &v1, const string &v2){
if (v1 < v2) return -1;
if (v2 < v1) return 1;
return 0;
}
int compare(const double &v1, const double &v2){
if (v1 < v2) return -1;
if (v2 < v1) return 1;
return 0;
}
/************函數模版定義*******************/
// template:關鍵字
// <typename T, typename Y, ...>:模版型參表
template <typename T>
int compare(const T &v1, const T &v2){
if (v1 < v2) return -1;
if (v2 < v1) return 1;
return 0;
}
/***********函數模版的使用******************/
int main () {
// T 是 int;
// 編譯器實例化: int compare(const int&, const int&)
// 函數模版實例化時會進行模板實參推斷
cout << compare(1, 0) << endl;
// T 是 string;
// 編譯器實例化: int compare(const string&, const string&)
string s1 = "hi", s2 = "world";
cout << compare(s1, s2) << endl;
compare ("hi", "world"); //會如何實例化?
return 0;
}
// 模版類型形參可以不同,但“<”必須存在
template <typename A, typename B>
int compare(const A& v1, const B& v2) {
if (v1 < v2) return -1;
if (v2 < v1) return 1;
return 0;
}
1.2、inline函數模版
template <typename T>
inline T min(const T&, const T&);
2、類模板
2.1、類模版的定義和使用
template <typename Type>
class Queue {
public:
Queue (); // 默認構造函數
Type &front (); // 返回頭元素的引用
const Type &front () const;
void push (const Type &); // 尾部添加
void pop(); // 頭部刪除
bool empty() const; // 隊列判斷
private:
// ...
};
// 實例化時必須顯式指定模板實參
Queue<int> qi;
Queue<vector<double> > qc;
Queue<string> qs;
3、模版形參
3.1、模版形參
模板形參可以是表示類型的類型形參,也可以是表示常量表達式的非類型形參。類型形參表示未知類型,非類型形參表示未知的值;模版形參的名字本身沒有特殊意義。
// T爲類型型參,N是非類型型參
// 每個模板類型形參前面必須帶上關鍵字 class 或 typename,每個非類型形參前面必須帶上類型名字
// 類型形參由關鍵字 class 或 typename 後接說明符構成。在模板形參表中,這兩個關鍵字具有相同的含義,都指出後面所接的名字表示一個類型。
template <class T, size_t N>
void array_init(T (&parm)[N]) {
for (size_t i = 0; i != N; ++i) {
parm[i] = 0;
}
}
int x[42];
double y[10];
array_init(x); // 編譯器實例化爲:array_init(int(&)[42])
array_init(y); // 編譯器實例化爲:array_init(double(&)[10])
// 非類型的型參,求值結果相同的表達式等價
int x[42];
const int sz = 40;
int y[sz + 2];
array_init(x); // 實例化: array_init(int(&)[42])
array_init(y); // 實例化: array_init(int(&)[42]),兩個函數調用引用是相同的實例
3.2、作用域和限制
//作用域
typedef double T;
template <typename T> T calc(const T &a, const T &b) {
T tmp = a;
// ...
return tmp;
}
template <typename T> T calc(const T &a, const T &b) {
typedef double T; // error
T tmp = a;
// ...
return tmp;
}
template <typename V, typename V> V calc(const V&, const V&); // error
4、模板編譯
當編譯器看到模板定義的時候,它不立即產生代碼。只有在看到用到模板時,如調用了函數模板或調用了類模板的對象的時候,編譯器才產生特定類型的模板實例。
當調用函數的時候,編譯器只需要看到函數的聲明。類似地,定義類類型的對象時,類定義必須可用,但成員函數的定義不是必須存在的。因此,應該將類定義和函數聲明放在頭文件中,而普通函數和類成員函數的定義放在源文件中。模板則不同:要進行實例化,編譯器必須能夠訪問定義模板的源代碼。當調用函數模板或類模板的成員函數的時候,編譯器需要函數定義,需要那些通常放在源文件中的代碼。
C++爲編譯模板代碼定義了兩種模型:包含模型和分別編譯模型。所有的編譯器都支持包含模型,只有部分編譯器支持分別編譯模型。
包含模型:
// header file utlities.h
#ifndef UTLITIES_H // header gaurd (Section 2.9.2, p. 69)
#define UTLITIES_H
template <typename T> int compare(const T&, const T&);
// other declarations
#include "utilities.cc" // get the definitions for compare etc.
#endif
// implemenatation file utlities.cc
template <typename T> int compare(const T &v1, const T &v2) {
if (v1 < v2) return -1;
if (v2 < v1) return 1;
return 0;
}
// other definitions
分別編譯模型:
// header file utlities.h
#ifndef UTLITIES_H // header gaurd (Section 2.9.2, p. 69)
#define UTLITIES_H
template <typename T> int compare(const T&, const T&);
// other declarations
#endif
// implemenatation file utlities.cc
export template <typename T> int compare(const T &v1, const T &v2) {
if (v1 < v2) return -1;
if (v2 < v1) return 1;
return 0;
}
// other definitions