1. 程式人生 > 其它 >C++知識點56——類模板(1、模板類的介紹)

C++知識點56——類模板(1、模板類的介紹)

技術標籤:C++基礎c++c++泛型演算法泛型

一、類模板

和函式模板一樣,類模板也得通過template關鍵字來宣告和定義,C++標準庫中有很多容器都是類模板

示例

template <typename T>
class mystack
{
public:
	mystack();
	mystack(const mystack<T> &rval);
	mystack<T> &operator=(const mystack<T> &rval);
	~mystack();
	void push(const T&ele);
	void pop();
	T top() const;
	bool empty() const {
		return elem.empty();
	}

private:
	vector<T> elem;
};

template <typename T>
mystack<T>::mystack()
{
	cout<<__func__<<endl;
}

template <class T>
mystack<T>::mystack(const mystack<T> &rval):elem(rval.elem)
{
	cout<<__func__<<endl;
}

template <class T>
mystack<T> & mystack<T>::operator=(const mystack<T> &rval)
{
	cout<<__func__<<endl;
	if (this==&rval) {
		return *this;
	}
	this->elem=rval.elem;
	return *this;
}

template <typename T>
mystack<T>::~mystack()
{
	cout<<__func__<<endl;
}

template <typename T> 
void mystack<T>::push(const T &elem)
{
	cout<<__func__<<endl;
	elem.push_back(elem);
}

template <typename T>
void mystack<T>::pop()
{
	cout<<__func__<<endl;
	try {
		elem.pos_back();
	}
	catch (out_of_range){
		cout<<"out_of_range"<<endl;
	}
}

template <typename T>
T mystack<T>::top() const
{
	cout<<__func__<<endl;
	try {
		return elem.back();
	}
	catch (out_of_range){
		cout<<"out_of_range"<<endl;
	}
}

上述程式碼通過vector定義了一個mystack模板類。模板類的定義依然得通過template和typename關鍵字

template <typename T>
class mystack
{
//......
};

此處的typename可以用class替換

類模板的模板引數可以用來宣告成員變數和成員函式。成員函式可以用在類內實現,也可以在類外實現,和普通類一樣,在類外實現時,要指定作用成員函式的作用域,只不過類的型別時mystack<T>,所以拷貝建構函式和operator=的形參型別都是mystack<T>的引用,作用域也因此是mystack<T>,因為有模板引數,所以,成員函式在類外實現時也要加template關鍵字和模板引數列表(template <typename T>)

二、使用類模板

int main(int argc, char const *argv[])
{
	mystack<int> si;
	return 0;
}

當例項化一個類模板時,沒有報錯,正常輸出,但是在main函式中新增si.push(10);後,編譯器提示以下錯誤

原因是形參elem沒有push_back操作,形參和成員變數重名了,修改後,輸出結果如下

也就是說,上述程式碼中的類成員函式在呼叫之前,沒報錯,被呼叫後,成員函式報錯了,所以,對於類模板來說,成員函式只有在被呼叫的時候才會例項化,而例項化相關的錯誤只有在例項化時才會出現,在編譯模板本身時並不會出現。類模板的成員函式只有在被呼叫時才會例項化可以節省空間和時間

三、類模板的特化

有時候,通用的類模板對於某些模板實參可能並不適合,這時候就需要對類模板進行特化。特化一個類模板是需要在其起始處新增template<>,然後接下來宣告特化類模板的型別

示例

template<>
class mystack<string>
{
public:
	mystack();
	mystack(const mystack<string> &rval);
	mystack<string> &operator=(const mystack<string> &rval);
	~mystack();
	void push(const string& ele);
	void pop();
	string top() const;
	bool empty() const {
		return elem.empty();
	}

private:
	deque<string> elem;
};

mystack<string>::mystack()
{
	cout<<__func__<<endl;
}

mystack<string>::mystack(const mystack<string> &rval):elem(rval.elem)
{
	cout<<__func__<<endl;
}

mystack<string> & mystack<string>::operator=(const mystack<string> &rval)
{
	cout<<__func__<<endl;
	if (this==&rval) {
		return *this;
	}
	this->elem=rval.elem;
	return *this;
}

mystack<string>::~mystack()
{
	cout<<__func__<<endl;
}

void mystack<string>::push(const string &ele)
{
	cout<<__func__<<endl;
	elem.push_back(ele);
}

void mystack<string>::pop()
{
	cout<<__func__<<endl;
	try {
		elem.pop_back();
	}
	catch (out_of_range &e){
		cout<<"out_of_range"<<endl;
	}
}

string mystack<string>::top() const
{
	cout<<__func__<<endl;
	try {
		return elem.back();
	}
	catch (out_of_range &e){
		cout<<"out_of_range"<<endl;
	}

	return "";
}

上述程式碼就是mystack的特化版本,大體上和類模板mystack沒差多少,但是容器型別從vector變為deque,捕獲異常的方式也變成捕獲異常的引用,如果仍然按照類模板的程式碼,會出警告

上述情況說明:1、特化版本的模板類可以和原來的類模板完全不同。2、特化類模板之後,會出現編譯原先類模板時不曾出現的錯誤,所以類模板的特化本質依舊是類模板的例項化

特化只是一種特殊的例項化,但是例項化的方式並不只有特化這一種

參考

《C++ Template》

《C++ Primer》

歡迎大家評論交流,作者水平有限,如有錯誤,歡迎指出