C++ Template用法總結

引言

模板(template)指C++程序設計語言中採用類型作爲參數的程序設計,支持通用程序設計。C++的標準庫提供許多有用的函數大多結合了模板的概念,如STL 以及 IO Stream。

函數模板

在C++入門中,很多人會接觸swap(int&, int&) 這樣的函數,類似代碼如下:

void swap(int&a , int& b) {
    int temp = a;
    a =  b;
    b = temp;
}

但是如果是要支持long,string,自定義class的swap函數,代碼和上述代碼差不多,只是類型不同,這個時候就是我們定義swap 的函數模板,就可以複用不同類型的swap函數代碼,函數模板的聲明形式如下:

template <class identifier> function_declaration;
template <typename identifier> function_declaration;

swap函數模板的聲明和定義代碼如下:

//method.h
template <typename T> void swap(T& t1, T& t2);

#include "method.cpp"
// method.cpp
template <typename T>
void swap(T &t1, T &t2){
	T tmpT; 
	tmpT = t1; 
	t1 = t2;
	t2 = tmpT;
}

上述是模板的聲明和定義了,那模板如何實例化呢,模板的實例化是編譯器做的事情,與程序員無關,那麼上述模板如何使用呢,代碼如下:

// main.cpp
#include <stdio.h>
#include "method.h"

int main(){
	// 模板方法
	int num1 = 1, num2 = 2;
	swap<int>(num1, num2);
	printf("num1:%d, num2:%d", num1, num2);
	return 0;
}

這裏使用 swap 函數,必須包含swap的定義,否則編譯會出錯,這個和一般的函數使用不一樣。所以必須在 method.h 文件的最後一行加入 “#include <method.cpp>”。

類模板

考慮我們寫一個簡單的棧的類,這個棧可以支持int類型,long類型,string類型等,不利用類模板,我們就要寫三個以上的stack類,其中代碼基本一樣,通過類模板,我們可以定義一個簡單的棧模板,再根據需要實例化爲int棧,long棧,string棧。

// stack.h
template <class T>
class Stack{
	private: 
		T *m_pT; 
		int m_maxSize;
		int m_size;
	public:
		Stack();
		~Stack();
		void push(T t);
		T pop();
		bool isEmpty();
}
#include "stack.cpp"
// stack.cpp
template <class T>
Stack<T>::Stack(){
	m_maxSize = 100;
	m_size = 0;
	m_pT = new T[m_maxSize];
}

template <class T>
Stack<T>::~Stack(){
	delete []m_pT;
}

template <class T>
void Stack<T>::push(T t){
	m_size ++;
	m_pT[m_size - 1] = t;
}

template <class T>
T Stack<T>::pop(){
	T t = m_pT[m_size - 1];
	m_size --;
	return t;
}

template <class T>
bool Stack<T>::isEmpty(){
	return m_size == 0;
}

上述定義了一個類模板–棧,這個棧很簡單,只是爲了說明類模板如何使用而已,最多隻能支持100個元素入棧,使用示例如下:

// main.cpp
#include <stdio.h>
#include "stack.h"

int main(){
	Stack<int> intStack;
	intStack.push(1);
	intStack.push(2);
	intStack.push(3);
	while(! intStack.isEmpty()){
		printf("num:%d\n", intStack.pop());
	}
	return 0;
}

模板參數

模板可以有類型參數,也可以有常規的類型參數int,也可以有默認模板參數,例如

template <class T, T def_val> class Stack{...}

上述類模板的棧有一個限制,就是最多隻能支持100個元素,我們可以使用模板參數配置這個棧的最大元素數,如果不配置,就設置默認最大值爲100,代碼如下:

// stack.h
template <class T, int maxsize = 100> class Stack{
	private:
		T *m_pT;
		int m_maxSize;
		int m_size;
	public:
		Stack();
		~Stack();
		void push(T t);
		T pop();
		bool isEmpty();
};

#include "stack.cpp"
// stack.cpp
template <class T, int maxsize> 
Stack<T, maxsize>::Stack(){
	m_maxSize = maxsize;
	m_size = 0;
	m_pT = new T[m_maxsize];
}

template <class T, int maxsize> 
Stack<T, maxsize>::~Stack(){
	delete []m_pT;
}

template <class T, int maxsize>
void Stack<T, maxsize>::push(T t){
	m_size ++;
	m_pT[m_size - 1] = t;
}

template <class T, int maxsize>
T Stack<T, maxsize>::pop(){
	T t = m_pT[m_size - 1];
	m_size --;
	return t;
}

template <class T, int maxsize>
bool Stack<T, maxsize>::isEmpty(){
	return m_size == 0;
}

使用示例如下:

// main.cpp
#include <stdio.h>
#include "stack.h"
int main(){
	int maxsize = 1024;
	Stack<int, 1024> intStack;
	for(int i=0; i<maxsize; i++){
		intStack.push(i);
	}
	while(!intStack.isEmpty()){
		printf("num:%d\n", intStack.pop());
	}
	return 0;
}

模板專門化

當我們要定義模板的不同實現,我們可以使用模板的專門化。例如我們定義的stack類模板,如果是char*類型的棧,我們希望可以複製char的所有數據到stack類中,因爲只是保存char指針,char指針指向的內存有可能會失效,stack彈出的堆棧元素char指針,指向的內存可能已經無效了。還有我們定義的swap函數模板,在vector或者list等容器類型時,如果容器保存的對象很大,會佔用大量內存,性能下降,因爲要產生一個臨時的大對象保存a,這些都需要模板的專門化才能解決。

函數模板專門化

假設我們swap函數要處理一個情況,我們有兩個很多元素的vector,在使用原來的swap函數,執行tmpT = t1要拷貝t1的全部元素,佔用大量內存,造成性能下降,於是我們系統通過vector.swap函數解決這個問題,代碼如下:

// method.h
template <class T> void swap(T &t1, T &t2);
#include "method.cpp"
#include <vector>
using namespace std;

template <class T>
void swap(T &t1, T &t2){
	T tmpT;
	tmpT = t1;
	t1 = t2;
	t2 = tmpT;
}

template <>
void swap(std::vector<int>& t1, std::vector<int>& t2){
	t1.swap(t2);
}

template<>前綴表示這是一個專門化,描述時不用模板參數,使用示例如下:

// main.cpp
#include <stdio.h>
#include <vector>
#include <string>
#include "method.h"

int main(){
	using namespace std;
	// 模板方法
	string str1 = "1", str2 = "2";
	swap(str1, str2);
	printf("str1:%s, str2:%s\n", str1.c_str(), str2.c_str());
	
	vector<int> v1, v2;
	v1.push_back(1);
	v2.push_back(2);
	swap(v1, v2);
	for(int i=0; i<v1.size(); i++){
		printf("v1[%d]: %d\n", i, v1[i]);
	}
	for(int i=0; i<v2.size(); i++){
		printf("v2[%d]: %d\n", i, v2[i]);
	}
	return 0;
}

vector的swap代碼還是比較侷限,如果要用模板專門化解決所有vector的swap,該如何做呢,只需要把下面代碼

template <>
void swap(std::vector<int>& t1, std::vector<int>& t2){
	t1.swap(t2);
}

改爲

template <class V>
void swap(std::vector<V>& t1, std::vector<V>& t2){
	t1.swap(t2);
}

就可以了,其他代碼不變。

類模板專門化,請看下面的 compare 代碼:

//compare.h
template <class T>
class compare{
	public:
		bool equal(T t1, T t2){
			return t1==t2;
		}
}
#include <iostream>
#include "compare.h"

int main(){
	using namespace std;
	char str1[] = "Hello";
	char str2[] = "Hello";
	compare<int> c1;
	compare<char *> c2;
	cout << c1.equal(1, 1) << endl; // 比較兩個int類型的參數
	cout << c2.equal(str1, str2) << endl; // 比較兩個char *類型的參數
	return 0;
}

在比較兩個整數,compare的equal方法是正確的,但是compare的模板參數是char*時,這個模板就不能工作了,於是修改如下:

// compare.h
#include <string.h>

template <class T>
class compare{
	public:
		bool equal(T t1, T t2){
			return t1==t2;
		}
};

template <>
class compare{
	public:
		bool equal(char* t1, char* t2){
			return strcmp(t1, t2) == 0;		
		}
}

main.cpp文件不變,此代碼可以正常工作。

模板類型轉換

還記得我們自定義的Stack模板嗎,在我們的程序中,假設我們定義了Shape和Circle類,代碼如下:

// shape.h
class Shape{
	
};
class Circle : public Shape{

};

然後我們希望可以這麼使用:

// main.cpp
#include <stdio.h>
#include "stack.h"
#include "shape.h"

int main(){
	Stack<Circle*> pcircleStack;
	Stack<Shape*> pshapeStack;
	pcircleStack.push(new Circle);
	pshapeStack = pcircleStack;
	return 0;
}

這裏是無法編譯的,因爲Stack<Shape*>不是Stack<Circle*>的父類,然而我們卻希望代碼可以這麼工作,那我們就要定義轉換運算符了,Stack代碼如下:

// stack.h
template <class T>
class Stack{
	public:
		Stack();
		~Stack();
		void push(T t);
		T pop();
		bool isEmpty();
		template <class T2> operator Stack<T2>();
	private:
		T *m_pT;
		int m_maxSize;
		int m_size;
};
#include "stack.cpp"
template <class T>
Stack<T>::Stack(){
	m_maxSize = 100;
	m_size = 0;
	m_pT = new T[m_maxSize];
}

template <class T>
Stack<T>::~Stack(){
	delete[] m_pT;
}

template <class T>
void Stack<T>::push(T t){
	m_size ++;
	m_pT[m_size - 1] = t;
}

template <class T>
T Stack<T>::pop(){
	T t = m_pT[m_size - 1];
	m_size --;
	return t;
}

template <class T>
bool Stack<T>::isEmpty(){
	return m_size == 0;
}

template <class T>
template <class T2> Stack<T>::operator Stack<T2>(){
	Stack<T2> StackT2;
	for(int i=0; i<m_size; i++){
		StackT2.push((T2)m_pT[m_size - 1]);
	}
	return StackT2;
}
// main.cpp
#include <stdio.h>
#include "stack.h"
#include "shape.h"

int main(){
	Stack<Circle*> pcircleStack;
	Stack<Shape*> pshapeStack;
	pcircleStack.push(new Circle);
	pshapeStack = pcircleStack;
	return 0;
}

這樣,Stack或者Stack<Circle*>就可以自動轉換爲Stack或者Stack<Shape*>,如果轉換的類型是Stack到Stack,編譯器會報錯。

其他

一個類沒有模板參數,但是成員函數有模板參數,是可行的,代碼如下:

class Util{
	public:
		template <class T> bool equal(T t1, T t2){
			return t1==t2;
		}
};

int main(){
	Util util;
	int a=1, b=2;
	util.equal<int>(a, b);
	return 0;
}

甚至可以把Util的equal聲明爲static,代碼如下:

class Util{
	public:
		template <class T> static bool equal(T t1, T t2){
			return t1==t2;
		}
};

int main(){
	int a=1, b=2;
	Util::equal<int>(1, 2);
	return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章