C++中的函數模板和類模板

前言

在我們進行大型程序的編寫時往往會遇到一類問題,同一個函數或類我們希望多種類型數據傳入時都能完成類似或者相同的功能,但是在C語言中我們很難做到這一點因爲我們往往在換了一個數據類型後就要重新寫一遍函數,這樣耽誤我們大量的時間,那麼有沒有一種語法在C++中能夠使讓我們的代碼成爲一種模板,不同的數據類型傳入也依然能夠執行類似的功能呢?

正所謂世界是由懶人創造的,於是在C++中引入了模板這一概念,同時也正得益於此使我們可以做到泛型編程,使我們的代碼極大程度的可以複用

一:泛型編程

之前我們實現一個通用的交換函數

void Swap(int& left, int& right){
	int temp = left;
	left = right;
	right = temp;
}
void Swap(double& left, double& right){
	double temp = left;
	left = right;
	right = temp;
}
void Swap(char& left, char& right){
	char temp = left;
	left = right;
	right = temp;
}

使用函數重載雖然可以實現通用的交換函數,但是也有以下的缺點:

  • 重載的函數僅僅只是類型不同,代碼的複用率比較低,只要有新類型出現時,就需要增加對應的函數
  • 代碼的可維護性比較低,一個出錯可能所有的重載均出錯

編寫與類型無關的通用代碼,是代碼複用的一種手段,模板是泛型編程的基礎。

二:模板

模板是實現泛型編程的基礎,其中又可細分爲函數模板類模板

編譯器根據調用函數模板和類模板的類型,實例化出對應的模板和類。

2.1 函數模板的概念

函數模板是生成一個家族的函數,根據實參類型產生函數的特定類型版本,其中我們可以使用任意類型的參數進行傳入

格式:template< class T >

#include <iostream>
using namespace std;
//定義函數模板,T爲任意類型
//class 也可替換爲 typename
template<class T>
void Swap(T& a, T& b){
    T t = a;
    a = b;
    b = t;
}

int main(){
    int a = 4, b = 5;
    double c = 6, d = 7;
    Swap(a, b);
    Swap(c, d);
    cout << a << " " << b << endl;
    cout << c << " " << d << endl;
}

2.2 函數模板的原理

函數模板並不是函數,在編譯器預處理階段,對於模板函數的使用,編譯器需要根據傳入的實參類型來推演生成對應類型的函數以供調用。

比如,當用double類型使用函數模板時,編譯器通過對實參類型的推演,將T確定爲double類型,然
後產生一份專門處理double類型的代碼。
在這裏插入圖片描述

2.3 函數模板的實例化

不同類型的參數使用函數模板時,稱爲函數模板的實例化。模板參數實例化分爲:隱式實例化和顯式實例化。

  • 隱式實例化:讓編譯器根據實參推演模板參數的實際類型
template<class T>
T Add(const T& left, const T& right){
	return left + right;
}

int main(){
	int a1 = 10, a2 = 20;
	double d1 = 10.0, d2 = 20.0;
	// 隱式實例化
	Add(a1, a2);
	Add(d1, d2);
	return 0;
}
  • 顯示實例化:在函數名後的<>中指定模板參數的實際類型
template<class T>
T Add(const T& left, const T& right){
	return left + right;
}

int main(){
	int a1 = 10, a2 = 20;
	double d1 = 10.0, d2 = 20.0;
	// 顯示實例化
	Add<int>(a1, d1);
	return 0;
}

2.4 模板參數的匹配原則

  1. 非模板函數可以和同名的函數模板同時存在,而且該函數模板還可以被實例化爲這個非模板函數
// 專門處理int的加法函數
int Add(int left, int right){
	return left + right;
}
// 通用加法函數
template<class T>
T Add(T left, T right){
	return left + right;
}
void Test(){
	Add(1, 2); // 與非模板函數匹配,編譯器不需要特化
	Add<int>(1, 2); // 調用編譯器特化的Add版本
}
  1. 對於非模板函數和同名函數模板,如果其他條件都相同,在調動時會優先調用非模板函數而不會從該模板產生出一個實例。如果模板可以產生一個具有更好匹配的函數, 那麼將選擇模板
// 專門處理int的加法函數
int Add(int left, int right){
	return left + right;
}
// 通用加法函數
template<class T1, class T2>
T1 Add(T1 left, T2 right){
	return left + right;
}
void Test(){
	Add(1, 2); // 與非函數模板類型完全匹配,不需要函數模板實例化
	Add(1, 2.0); // 模板函數可以生成更加匹配的版本,編譯器根據實參生成更加匹配的Add函數
}

2.5 類模板的概念

類模板與函數模板類似,是使用一個模板參數來構造整個類,並且原理也與函數模板類似,只有在類構造對象時纔會推演模板參數進行實例化,實例化出我們想要的類。

template<class T>
class Vector{
publicVector(size_t capacity = 10)
		: _pData(new T[capacity])
		, _size(0)
		, _capacity(capacity)
	{}
	// 使用析構函數演示:在類中聲明,在類外定義。
	~Vector();
	void PushBack(const T& data)void PopBack()// ...
	size_t Size() {return _size;}
	T& operator[](size_t pos){
		assert(pos < _size);
		return _pData[pos];
	}
private:
	T* _pData;
	size_t _size;
	size_t _capacity;
};
// 注意:類模板中函數放在類外進行定義時,需要加模板參數列表
template <class T>
Vector<T>::~Vector(){
	if(_pData)
	delete[] _pData;
	_size = _capacity = 0;
}

2.6 類模板的實例化

類模板實例化與函數模板實例化不同,類模板實例化需要在類模板名字後跟<>,然後將實例化的類型放在<>中即可,類模板名字不是真正的類,而實例化的結果纔是真正的類。

// Vector類名,Vector<int>纔是類型
Vector<int> s1;
Vector<double> s2;
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章