目錄
模板初階
泛型編程
函數模板
模版概念
函數模版格式
模版的原理
函數模板的實例化
模版參數的匹配規則
類模板
模板初階
泛型編程
使用函數重載雖然可以實現,但是有一下幾個不好的地方:
1. 重載的函數僅僅是類型不同,代碼復用率比較低,只要有新類型出現時,就需要用戶自己增
加對應的函數。
2. 代碼的可維護性比較低,一個出錯可能所有的重載均出錯。
如果在C++中,也能夠存在這樣一個模具,通過給這個模具中填充不同材料(類型),來獲得不同
材料的鑄件(即生成具體類型的代碼),那將會節省許多頭發。巧的是前人早已將樹栽好,我們只
需在此乘涼。
泛型編程:編寫與類型無關的通用代碼,是代碼復用的一種手段。模板是泛型編程的基礎。
void Swap(int& left, int& right)
{int c = left;left = right;right = c;
}void Swap(double& left, double& right)
{double c = left;left = right;right = c;
}void Swap(char& left, char& right)
{char c = left;left = right;right = c;
}
函數模板
模版概念
函數模板代表了一個函數家族,該函數模板與類型無關,在使用時被參數化,根據實參類型產生函數的特定類型版本。
函數模版格式
template<typename T1, typename T2,…,typename Tn>返回值類型 函數名(參數列表){}
下面是一個交換函數的模版,可以對不同的類型進行一個交換的操作
template<typename T>
void Swap(T& left, T& right)//針對廣泛的類型
{T temp = left;left = right;right = temp;
}
int main()
{int x = 0, y = 1;double m = 1.1, n = 2.2;//通過模版就很方便了//調用的其實還是兩個函數Swap(x, y);Swap(m, n);return 0;
}
注意: typename 是 用來定義模板參數 關鍵字 , 也可以使用 class( 切記:不能使用 struct 代替class)。
模版的原理
函數模板是一個藍圖,它本身并不是函數,是編譯器用使用方式產生特定具體類型函數的模具。
所以其實模板就是將本來應該我們做的重復的事情交給了編譯器。
在編譯器編譯階段,對于模板函數的使用,編譯器需要根據傳入的實參類型來推演生成對應類型的函數以供調用。比如:當用double類型使用函數模板時,編譯器通過對實參類型的推演,將T確定為double類型,然后產生一份專門處理double類型的代碼,對于字符類型也是如此。
函數模板的實例化
編譯器通過模版生成對應的函數叫做實例化操作
實例化分為兩種,一種叫做隱式實例化,一種叫做顯式實例化
如果我們傳的是兩個類型不一樣的數據的話是會報錯的,但是我們可以通過增加多個模版參數來進行不同類型數據的交換的
template<class T>
T Add(const T& left, const T& right)
{return left + right;
}template<class T>
T* Func(size_t n)
{return new T[n];
}int main()
{int a1 = 10, a2 = 20;double c1 = 10.1, c2 = 20.1;//自動推倒類型 隱式實例化Add(a1, a2);Add(c1, c2);cout << Add(a1, (int)c2) << endl;cout << Add(c1, (double)a2) << endl;//顯示實例化cout << Add<int>(a1, c2) << endl;cout << Add<double>(c1, a2) << endl;Func<int>(10);Func<double>(20);return 0;
}
模版參數的匹配規則
●?一個非模板函數可以和一個同名的函數模板同時存在,而且該函數模板還可以被實例化為這個非模板函數。
●?對于非模板函數和同名函數模板,如果其他條件都相同,在調動時會優先調用非模板函數而不會從該模板產生出一個實例。如果模板可以產生一個具有更好匹配的函數, 那么將選擇模板。
●?模板函數不允許自動類型轉換,但普通函數可以進行自動類型轉換。
int Add(int left, int right)
{return left + right;
}template<class T>
T Add(T left,T right)
{return (left + right)*5;
}template<class T1,class T2>
T1 Add(T1 left,T2 right)
{return(left+right)*10
}int main()
{cout << Add(1, 2) << endl; //調用一cout << Add<int>(1, 2) << endl; //調用二cout << Add(1.1, 2.2) << endl; //不匹配一,直接調用二cout << Add(1, 2.2) << endl; //調用三 return 0;
}
類模板
template<class T1, class T2, ..., class Tn>
class 類模板名
{// 類內成員定義
};
// 類模板
template<typename T>
class Stack
{
public:Stack(size_t capacity = 4){_array = new T[capacity];_capacity = capacity;_size = 0;}void Push(const T& data);private:T* _array;size_t _capacity;size_t _size;
};// 聲明和定義分離的寫法
template<class T>
void Stack<T>::Push(const T& data)
{// ..._array[_size++] = data;
}int main()
{// 實例化生成對應的類,這里是兩個不同的類型Stack<int> st1; // intStack<double> st2; // double// Stack是類名,Stack<int>才是類型Stack<char> st3;return 0;
}
模版不建議聲明和定義分離到兩個文件.h 和.cpp會出現鏈接錯誤。
只有在類里面我們才能使用這個T,出了這個類就不作數了。
類模板實例化與函數模板實例化不同,類模板實例化需要在類模板名字后跟<>,然后將實例化的類型放在<>中即可,類模板名字不是真正的類,而實例化的結果才是真正的類。