1. 程式人生 > >C++模板初階(泛型程式設計,函式模板,類模板)

C++模板初階(泛型程式設計,函式模板,類模板)

1.泛型程式設計

怎麼實現一個通用的交換函式呢

使用函式過載雖然可以實現,但是有一下幾個不好的地方:

1. 過載的函式僅僅只是型別不同,程式碼的複用率比較低,只要有新型別出現時,就需要增加對應的函式

2. 程式碼的可維護性比較低,一個出錯可能所有的過載均出錯

那能否告訴編譯器一個模子,讓編譯器根據不同的型別利用該模子來生成程式碼呢?

所以就引出了泛型程式設計的概念

泛型程式設計:編寫與型別無關的通用程式碼,是程式碼複用的一種手段。模板是泛型程式設計的基礎

模板分為:

1.函式模板

2.類模板

2.函式模板

2.1函式模板的概念:

函式模板代表了一個函式家族,該函式模板與型別無關,在使用時被引數化,根據實參型別產生函式的特定型別版本

2.2 函式模板的格式:

template<typename T1, typename T2,......,typename Tn>

返回值型別 函式名(引數列表){}

注意:typename是用來定義模板引數關鍵字,也可以使用class(切記:不能使用struct代替class)

2.3函式模板的原理

模板是一個藍圖,它本身並不是函式,是編譯器用使用方式產生特定具體型別函式的模具。所以其實模板就是將本來應該我們做的重複的事情交給了編譯器

在編譯器編譯階段,對於模板函式的使用,編譯器需要根據傳入的實參型別來推演生成對應型別的函式以供 呼叫。比如:當用double型別使用函式模板時,編譯器通過對實參型別的推演,將T確定為double型別,然 後產生一份專門處理double型別的程式碼,對於字元型別也是如此。

2.4函式模板的例項化

用不同型別的引數使用函式模板時,稱為函式模板的例項化。模板引數例項化分為:隱式例項化和顯示例項化

1.隱式例項化:讓編譯器根據實參推演模板引數的實際型別

#include <iostream>
using namespace std;

template<class T>
T Add(const T& left, const T& right)
{
	return left + right;
}
int main()
{
	int a1 = 10;
	int a2 = 20;
	double b1 = 10.0;
	double b2 = 20.0;
	Add(a1, a2);
	Add(b1, b2);

	//Add(a1, b1);//這句不能通過編譯,因為在編譯期間,當編譯器看到
				//該例項化時,需要推演其實參型別通過實參a1將T推演
				//為int,通過實參d1將T推演為double,但模板引數列表
				//只有一個T,編譯器無法確定此處到底該將T確定為int
				//或者double型別
	//注意:在模板中,編譯器一般不會進行類別轉換操作,因為一旦轉換出了問題,編譯器就要背鍋

	//此時有兩種方式:1.使用者自己來強制轉化 2.使用顯式例項化
	Add(a1, (int)b1);

	return 0;
}

2.顯式例項化:在函式後的<>中指定模板引數的實際型別


int main()
{
	int a = 10;
	double b = 20.0;

	//顯式例項化
	Add<int>(a, b);
	return 0;
}

如果型別不匹配,編譯器會嘗試進行隱式型別轉換,若果無法轉換成功編譯器將會報錯

2.5 模板引數的匹配原則

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版本
}

2.對於非模板函式和同名函式模板,如果其他條件都相同,在調動時會優先呼叫非模板函式而不會從該模板產生出一個例項。如果模板可以產生一個具有更好匹配的函式,那麼將選擇模板

//專門處理int的加法函式
int Add(int left, int right)
{
	return left + right;
}

//通用加法函式
template<class T1,class T2>
T Add(T1 left, T2 right)
{
	return left + right;
}

void Test()
{
	Add(1, 2);//與非模板函式匹配,不需要函式模板例項化
	Add(1, 2.0);//模板函式可以生成更加匹配的版本,編譯器
				//根據實參生成更加匹配的Add函式
}

3.顯式指定一個空的模板實參列表,該語法告訴編譯器只有模板才能來匹配這個呼叫,而且所有的模板引數都應該根據實參演繹出來

//專門處理int的加法函式
int Add(int left, int right)
{
	return left + right;
}

//通用加法函式
template<class T,class T>
T Add(T left, T right)
{
	return left + right;
}

void Test()
{
	Add(1, 2);//與非模板函式匹配,不需要函式模板例項化
	Add<>(1,2);//呼叫模板生成的Add函式
}

4.模板函式不允許自動型別轉換,但普通函式可以進行自動型別轉換

3.類模板

3.1類模板的定義格式

template<class T1,class T2,...,class Tn>
class 類模板名
{
	//類內成員定義
};

//動態順序表
template<class T>
class Vector
{
public:
	Vector(size_t capacity = 10)
	:_pData(new T[capacity])
	, _size(0);
	, _capacity(capacity)
	{}

	//在類中宣告,在類外定義
	~Vector();

	void PushBack(const T& data)
	{
		//_checkCapacity();
		_pData[_size++] = data;
	}
	void PopBack()
	{
		--_size;
	}
private:
	T* _pData;
	size_t _size;
	size_t _capacity;
};

//注意,類模板中函式放在類外進行定義時,需要加模板版引數列表
template<class T>
Vector<T>::~Vector()
{
	if (_pData)
	{
		delete[] _pData;
	}
}

3.2 類模板的例項化

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

//Vector類名,Vector<int>才是型別
Vector<int> s1;
s1.PushBack(1);
s1.PushBack(2);

Vector<double> s2;
s2.PushBack(1.0);
s2.PushBack(2.0);