【C++基礎十】泛型編程—模板
- 1.什么是模板
- 2.函數模板的實例化:
- 2.1隱式實例化
- 2.2顯示實例化
- 3.函數模板參數的匹配規則
- 4.什么是類模板
- 5.類模板的實例化
- 6.聲明和定義分離
1.什么是模板
void swap(int& a, int& b)
{int tmp = 0;tmp = a;a = b;b = tmp;
}void swap(double& a, double& b)
{double tmp = 0;tmp = a;a = b;b = tmp;
}void Swap(char& a, char& b)
{char temp = a;a = b;b = temp;
}
正常來說,對于不同類型的變量進行交換,需要實現不同的swap函數,這樣實現有些太繁瑣了
為了解決相似函數的不同調用問題,C++提出泛型編程,編寫與類型無關的通用代碼,實現代碼復用,即模板
模板主要分為函數模板和類模板
模板格式:
template <typename T1, typename T2,…,typename Tn>//一次性可以定義多個類型
typename是用來定義模板參數的關鍵字,也可以使用class,兩者目前是沒區別的,但是由于STL大部分用的class,所以建議使用class
template <class T1, class T2,…,class Tn>//一次性可以定義多個類型
swap函數模板:
template <class T>
void Swap(T& a, T& b)
{T temp = a;a = b;b = temp;
}
寫好上面的代碼后,傳int類型的變量進去,T就會被實例化為int,以此類推
2.函數模板的實例化:
2.1隱式實例化
實參傳給形參后,編譯器自動推演模板類型
template <class T>
T add(T& left, T& right)
{return left + right;
}
int main()
{int a = 1;int b = 2;double p1 = 1.0;double p2 = 2.0;//同類型進行可以正常運行add(a, b);//自動推演類型為intadd(p1, p2);//自動推演類型為double//-----------addd(a, p1);//a與p1是不同類型,會報錯return 0;
}
不同類型去模板推演會出現歧義,a傳過去將T推演成int,而p1傳過去把T推演成double,T無法確定推演int還是double
2.2顯示實例化
在函數名后的<>中指定模板參數的類型
template <class T>
T Add(const T& left, const T& right)
{return left + right;
}
int main()
{int a1 = 10, a2 = 20;double d1 = 10.1, d2 = 20.1;Add<int>(a1, d1);//顯示實例化
}
指定T的類型為int ,因為d1是double類型,所以在傳參時會發生隱式類型轉換變成int,若無法轉換成功編譯器將會報錯
3.函數模板參數的匹配規則
模板函數和普通函數可以同時存在
通過調試可以發現調用的是普通函數,因為成本更低,使用模板還需要實例化生成代碼,而普通函數可以直接使用
// 專門處理int的加法函數
int Add(int left, int right)
{return left + right;
}
// 通用加法函數
template<class T>
T Add(T left, T right)
{return left + right;
}int main()
{Add(10,20)//調用非模板Add(11.1,6.3);//調用模板return 0;
}
在調用函數時若參數和非模板函數匹配,那么編譯器會優先調用非模板函數
若非模板函數不匹配或模板函數更匹配,那么編譯器會優先調用模板函數
4.什么是類模板
template <class T1, class T2, …, class Tn>//和函數模板類似,類模板也可以同時定義多個模板參數
class 類模板名
{// 類內成員定義
};
有typedef的存在為什么還有類模板?
typedef int STdatatype;
class stack
{
private:STdatatype* _a;size_t top;size_t capacity;
};int main()
{stack s1;//想要S1存儲intstack s2;//想要S2存儲doublereturn 0;
}
如果想要改變棧儲存的類型可以選擇改變typedf定義的類型
但是若想要兩個棧分別儲存不同的數據類型typedef做不到
兩份類的代碼幾乎是一致的,但若想達到目的就需要再拷貝一份出來,就太繁瑣了
一個簡易的順序表:
所有實際類型需要出現的地方用T代替
template<class T>
class Vector
{
public :Vector(size_t capacity = 10): _Data(new T[capacity]), _size(0), _capacity(capacity){}T& operator[](size_t pos){assert(pos < _size);return _Data[pos];}
private:T* _Data;size_t _size;size_t _capacity;
};
5.類模板的實例化
類模板只能顯示實例化
,這樣就可以達到s1存儲int,S2存儲double
template <class T>
class stack
{
public:stack(int capacity=4){_a = new T[capacity];_top = 0;_capacity = capacity;}~stack(){delete[]_a;_capacity = _top = 0;}
private:T* _a;size_t top;size_t capacity;
};int main()
{stack <int>s1;//想要S1存儲intstack <double>s2;//想要S2存儲doublereturn 0;
}
6.聲明和定義分離
對于模板,
vector
是類名而不是類型,加上實例化的模板參數后vector<T>
才是類型
析構函數在類外面定義 ,需要使用類型vector < T>
,而T作為模板需要調用template < class T >
必須要再加上類模板template并且要指定類域
template<class T>
class Vector
{
public:Vector(size_t capacity = 10): _pData(new T[capacity]), _size(0), _capacity(capacity){}~Vector();//類中的聲明析構函數void push_back(T x);//類中聲明函數
private:T* _Data;size_t _size;size_t _capacity;
};template <class T>//析構函數在類外面定義 要加上模板
Vector<T>::~Vector()
{detele[]_pData;_pData = nullptr;_size = _capacity = 0;
}template<class T>//模板類的函數在類外定義,要加上模板
void Vector<T>::push_back(T x)
{_Date[_size] = x;_size++;
}int main()
{Vector<int> v;return 0;
}