1. 程式人生 > >深入淺出理解:函式模板與類模板、存在的實際意義以及使用方法,

深入淺出理解:函式模板與類模板、存在的實際意義以及使用方法,

在講模板之前,先閒扯一下吧:C++最重要的特性之一就是,程式碼的重用,為了實現程式碼重用,程式碼就必須具有通用性

通用程式碼不受資料型別的影響,並且可以自動適應資料型別的變化,這種程式設計型別稱為引數化程式設計

模板是C++支援引數化程式設計的工具,通過它可以實現引數化多型性。

所謂引數化多型性是指:將程式所處理的物件的型別引數化,使得一段程式可以處理多種不同型別的物件。

簡單的理解,模板就是為了讓程式碼的通用性更強。有了以上的理解, 下面理解函式模板和類模板就輕鬆多了。

(2)函式模板

 要講函式模板,就得先講一講函式過載,相信大家都知道,函式過載通常是對於不同的資料型別完成類似的操作。

在很多時候,一個演算法其實是可以處理多種資料型別的。但是用函式實現演算法時,即使設計為過載函式也只是

使用相同的函式名,函式體仍然要分別定義。下面舉一個求絕對值的函式過載的例項,方便大家理解。

int abs(int x)

{

return x<0?-x:x;

}

double abs(double x)

{

return x<0?-x:x;

}

可以看出,這兩個函式的功能和函式體是一模一樣的,但是為了實現對不同資料型別的資料求絕對值操作,我們不得不寫兩個

同名函式來實現操作,如果能寫一段通用程式碼適用與多種不同資料型別該多好呢,這會使程式碼的可重用性大大提高,從而提高

軟體的開發效率。所以聰明的人們便設計出了這麼一個工具,那就是函式模板,程式設計師只需對函式模板編寫一次,然後基於調

用函式時提供的引數型別,C++編譯器將自動產生相應的函式來正確處理該型別的資料。

函式模板的定義形式如下:

template<模板引數列表>

型別名  函式名(引數列表)

{

函式體的定義;

}

說明:class或者typename識別符號,指明可以接收一個型別引數,這些型別引數代表的是型別,可以是系統預定義的資料型別,也

可以是自定義型別。型別引數可以用來指定函式模板本身的形參型別、返回值型別,以及宣告函式中的區域性變數。

下面舉一個求絕對值的函式模板的程式。

#include<iostream>

using namespace std;

template<typename T>

T  abs(T x)

{

return x<0?-x:x;

}

int  main()

{

int n_number=-5;

double d_number=-5.5;

cout<<abs(n_number)<<ebdl;

cout<<abs(d_number)<<endl;

return 0;

}

執行結果如下:

在main函式中兩次呼叫abs()函式,

對於呼叫表示式abs(n_number)時,由於n_number為int型別,所以推匯出模板中型別引數T為int;

當呼叫表示式abs(d_number)時,由於d_number為double型別,所以推匯出模板型別引數T為double;

當型別引數的含義確定後,編譯器將以函式模板作為一個樣板,生成一個函式,這一過程便是傳說中的函式模板例項化;

其實編譯器會例項化以下兩個函式:

int abs(int x)

{

return x<0?-x:x;

}

double  abs(double x)

{

return x<0?-x:x;

}

下面來一個稍微複雜一點的函式模板的例子,供各位深入理解一下函式模板的用法。

#include<iostream>

using namespace  std;

template<class T>

void outputArray(const T *array,int count)

{

for(int i=0;i<count;i++)

{

cout<<array[i]<<" ";

}

cout<<endl;

}

int main()

{

const  int  a_count=8,b_count=8,c_count=20;

int    a[a_count]={1,2,3,4,5,6,7,8};

double    b[b_count]={1.1,2.2,3.3,4.4,5.5,6.6,7.7,8.8};

char   c[c_count]="Welcome to see you!";

cout<<"陣列a:"<<endl;

outputArray(a,a_count);

cout<<"陣列b:"<<endl;

outputArray(b,b_count);

cout<<"陣列c:"<<endl;

outputArray(c,c_count);

return 0;

}

執行效果如下圖:

(3)類模板

類模板可以使使用者為類定義一種模式,使得類中的某些資料成員、成員函式、返回值或者區域性

變數能取任意型別。(包括系統預定義和使用者自定義的)

如果說類是對一組物件公共性質的抽象,而類模板則是對不同類的公共性質的抽象,所以說類模板是更高層次的抽象。

類模板宣告的語法形式:

template<模板引數表>

class  類名

{

類成員宣告;

}

其中類成員的宣告方法與普通類的宣告方法其實是一模一樣的,只是它的各個成員(資料成員和函式成員)通常要用到模板的型別引數T;

注意:如果要在類模板以外定義成員函式,要採用以下形式

template <模板引數表>

型別名  類名<模板引數識別符號列表>::函式名(引數表)

一個類模板宣告,它自身並不是一個類,它說明類的一個家族,只有當它被其它程式碼引用時,模板才根據需要生成具體的類。

使用一個模板類來建立物件時,格式如下:

模板名<模板引數表>物件名1,....物件名N;

下面舉個例子,方便大家理解,

本例中,宣告一個實現任意型別資料存取的類模板Store,然後通過具體資料型別引數對類模板進行例項化,

生成類,然後類再被例項化,

生成物件s1,s2,s3,d.

源程式如下:

#include<iostream>
#include<cstdlib>
using namespace  std;

struct Student
{
	int id;
	float gpa;
};
template<class T>
class Store
{
	private:
		T item;
		bool haveValue;
	public:
		Store();
		T& getElem();
		void putElem(const T &x);			
};
template<class T>
Store<T>::Store():haveValue(false){}

template<class T>
T& Store<T>::getElem()
{
	if(!haveValue)
	{
		cout<<"沒有資料"<<endl;
		exit(1); 
	}
	return item;
}

template<class T>
void Store<T>::putElem(const T &x)
{
	haveValue=true;
	item=x;
}

int main()
{
	Store<int>  s1,s2;
	s1.putElem(3);
	s2.putElem(-7);
	cout<<s1.getElem()<<"  "<<s2.getElem()<<endl;

	Student g={101011,99};
	Store<Student>s3;
	s3.putElem(g);
	cout<<"The student ID is:  "<<s3.getElem().id<<endl;
	
	Store<double>d;
	cout<<d.getElem()<<endl;
		
	return 0;
}
執行結果: