模板:就是與類型無關的邏輯代碼
模板函數:就是要實現和類型無關的函數
那麼爲什麼會有模板這個東西呢?
我們在實現swap函數和isequal函數,他們都是一些與類型相關的函數,並且如果我們要根據類型實現多個swap函數的化那麼代碼的重複率就會特別高,此時我們就可以用模板來實現此函數
我們可以先寫一個交換函數的栗子
#include <iostream>
using namespace std;
template <class T>
void swap(T *a,T *b)
{
T tmp = *a;
*a = *b;
*b = tmp;
}
int main()
{
int a = 1;
int b = 2;
swap(a,b);
cout<<a <<b <<endl;
char c = 'c';
char d = 'd';
swap(c,d);
cout<<c <<d <<endl;
return 0;
}
但是此時如果模板參數不匹配的話那就不能調用此函數
模板函數的重載
模板函數是可以重載的,我們可以看一下比較相等的這個栗子
template <class T1,class T2>
bool IsEqual(const T1& left,const T2& right)
{
return left == right;
}
template<class T>
bool IsEqual(const T& left,const T& right)
{
return left == right;
}
int main()
{
cout<<IsEqual<int>(1,1)<<endl;
cout<<IsEqual<int,float>(1,1.2)<<endl;
return 0;
}
針對模板函數我們可以畫個圖來理解一下,模板都是編譯器進行推演,生成相對應類型的代碼,然後在進行編譯
模板類:實現和類型無關的類——容器
先舉個栗子:順序表和鏈表(Vector,List)
可以先看代碼
我們寫模板類和我們普通類的代碼的時候最不一樣的是類型的問題,記住以下:
順序表
//vector是類名
//vector<T>是類型
#include <iostream>
#include <stdio.h>
#include <malloc.h>
#include <assert.h>
#include <string.h>
using namespace std;
template<class T>
//vector是類名
//vector<T>是類型
class Vector
{
public:
Vector();
~Vector();
Vector(const Vector<T>& v);
Vector<T>& operator=(const Vector<T>& v);
size_t Size();
size_t Capacity();
void PushBack(const T& x);
void PopBack();
void Expand(size_t n);
void Insert(size_t pos,const T& x);
//刪除指定位置的值
void Erase(size_t pos);
void Print();
const T& Back() const;
bool Empty();
protected:
T *_start;
T *_finish;
T *_endofstorage;
};
鏈表
#include <iostream>
#include <stdio.h>
#include <assert.h>
using namespace std;
template <class T>
struct ListNode
{
ListNode<T> *_prev;
ListNode<T> *_next;
T _data;
ListNode(const T& x)
:_prev(NULL)
,_next(NULL)
,_data(x)
{}
};
template <class T>
class List
{
typedef ListNode<T> Node;
public:
List();
~List();
//從頭上刪除一個節點
void Pop();
//取鏈表頭結點
const T& Front();
void PushBack(const T& x);
void PopBack();
//在pos之前插入
void Insert(Node *pos,const T& x);
void Erase(Node *pos);
size_t Size();
bool Empty();
protected:
Node *_head;
};
模板類的推演過程(我們用順序表來舉例子,其他的模板類都是一樣的)
模板類——適配器
我們來看兩個代碼棧和隊列,他們都是要用我們以上說過的順序表和鏈表來當他們的容器
先看代碼
棧
#include<stdio.h>
#include<iostream>
#include<vector>
#include"TempVector.h"
using namespace std;
//#pragma once
//模板類的模板
//Container:容器
//Stack:適配器
template <class T,class Container>
class Stack{
public:
void Push(const T& x){
_con.PushBack(x);
}
void Pop(){
_con.PopBack();
}
//T& Top(){
// return _con.Back();
//}
const T& Top() const{
return _con.Back();
}
size_t Size(){
return _con.Size();
}
bool Empty(){
return _con.Empty();
}
protected:
Container _con;
};
然後我們用Stack來畫圖理解一下他的實現過程
模板類的模板參數
當我們用上面的適配器都爲時候,我們在傳參數的時候,如果我們傳給容器的參數如果傳的和我們適配器的類型不相同的話,就會出現一些問題,舉個栗子
Queue<int , List<char>>
c++爲了防止這種情況的發生,給我們提供了另外一種操作就是模板類的模板參數
我們來看一下代碼啦啦啦
隊列
#pragma once
#include<stdio.h>
#include<iostream>
#include"../TempletList/TempletList.h"
using namespace std;
//模板類的模板參數
template <class T, template <class> class Container>
class Queue
{
public:
//往隊尾插入節點
void Push(const T& x)
{
_con.PushBack(x);
}
//刪除隊首節點
void Pop()
{
_con.Pop();
}
//取隊首節點
const T& Front()
{
return _con.Front();
}
bool Empty()
{
return _con.Empty();
}
size_t Size() const
{
return _con.Size();
}
protected:
Container _con;
};
調用此模板類
Queue<int ,List > q;//如此調用即可
那我們在畫個圖來理解一下這個的原理吧
有興趣的可以去看一下
非類型的模板參數
當我們寫一個靜態順序表的時候,它的大小都是固定的,那麼有些要存得數據較小,而有些就要存的數據較多,那要時用這個較大的順序表來存儲這個較小的數據,就會造成空間浪費,那麼這個時候就可以用到我們非類型的模板參數
#include<iostream>
using namespace std;
template<class T, size_t MAXSIZE = 10 >
class SeqList
{
public:
SeqList();
protected:
T _data[MAXSIZE];
size_t size;
};
template<class T, size_t MAXSIZE>
SeqList<T,MAXSIZE>::SeqList()
:size(0)
{}
void test()
{
SeqList<int> s1;
SeqList<int , 20> s2;
}
int main()
{
test();
return 0;
}
要注意的是浮點數和類對象不能做非類型的模板參數
類模板的特化——全特化和偏特化
全特化就是限定死模板實現的具體類型,偏特化就是如果這個模板有多個類型,那麼只限定其中的一部分。
模板函數只能是全特化,不能是偏特化
模板類全特化,偏特化都可以
全特化
//普通模板類
template <class T>
class SeqList
{
public:
SeqList();
~SeqList();
protected:
int _size;
int _capacity;
T *_data;
};
//特化版本
template <>
class SeqList<int>
{
public:
SeqList();
~SeqList();
protected:
int _size;
int _capacity;
int *_data;
};
template <>
class SeqList<char>
{
public:
SeqList();
~SeqList();
protected:
int _size;
int _capacity;
char *_data;
};
偏特化
//普通模板類
template <class T1,class T2>
class Date
{
public:
Date();
protected:
T1 _d1;
T2 _d2;
};
//偏特化第二個類型
template <class T1>
class Date<T1,int>
{
public:
Date();
protected:
T1 _d1;
int _d2;
};
我們會發現偏特化不僅僅是指特殊部分參數,還是針對模板參數進行進一步限制得出來的另一個限制版本
並且如果有特化版本的類,那麼編譯器就會直接走特化版本就不需要在進行推演
模板總結:
優點
模板複用了代碼,節省了資源,C++的模板庫STL就由此產生
增強了代碼的靈活性
缺點:
會增加編譯器的負擔要進行推演
代碼變複雜不易定位錯誤