天天看點

7、群體類和群體資料的組織-1.函數模闆和類模闆

基本資料類型是c++編譯系統預定義的,而自定義類型的資料是由多個基本類型或自定義類型的元素組成的,我們稱之為群體資料。

對于群體資料,僅有系統預定義的操作是不夠的,在很多情況下,還需要設計與某些具體問題相關的特殊操作,并按照面向對象的方法将資料與操作封裝起來,這就是群體類。

群體可以分為兩種:線性群體和非線性群體。線性群體中的元素按位置排列有序。非線性群體不用位置順序來辨別元素。

關于群體資料的組織是屬于資料結構的範疇,這裡隻介紹兩類常用的算法:排序和查找方法。

排序:又稱為分類或整理,是将一個無序序列調整為有序的過程。在排序的過程中需要完成兩種基本操作:一是比較兩個數的大小,二是調整元素在序列中的位置。排序方法:直接插入排序、直接選擇排序和起泡排序。

查找:是在一個序列中按照某種方式找出需要的特定資料元素的過程。方法:順序查找、折半查找。

1、函數模闆與類模闆

通用代碼需要不受資料類型的影響,并且可以自動适應資料類型的變化。這種程式設計類型稱為參數化程式設計。

是c++支援參數化程式設計的工具,通過它可以實作參數化多态性。

所謂參數化多态性,就是将程式所處理的對象的類型參數化,使得一段程式可以用于處理多種不同類型的對象。

1)函數模闆

程式員隻需對函數模闆編寫一次,然後基于調用函數時提供的參數類型,c++編譯器将自動産生相應的函數來正确處理該類型的資料。

函數模闆的定義形式是:

template <class T> 或template <typename T>

類型名 函數名(參數表)

{函數體的定義}

所有函數模闆的定義都是用關鍵字template開始的,該關鍵字之後是使用尖括号<>括起來的類型參數表。每一個類型參數(例如上面文法形式的T)之前都有關鍵字class或關鍵字typename,這些類型參數代表的是類型,可以是内部類型或自定義類型。這樣類型參數就可以用來指定函數模闆本身的形參類型和傳回值類型。以及聲明函數中的局部變量。函數體的定義方式跟其他類似的。

#include<iostream>
using namespace std;
template<typename T>
T abs(T x)
{
  return x<0?-x:x;
}
int main()
{
  int n=-5;
  double d=-5.5;
  cout<<abs(n)<<endl;
  cout<<abs(d)<<endl;
}      

在上述主函數中調用abs()時,編譯器從實參的類型推導出函數模闆的類型參數。當類型參數的含義确定後,編譯器将以函數模闆為樣闆,生成一個對應實參類型的一個函數。

例子:

#include<iosteam>
using namespace std;
template <class T>  //聲明函數模闆
void outputArray(const T *P_array,const int count) //定義函數體
{
  for(int i=0;i<count;i++)
  {
    cout<<P_array[i]<<"";
  }
  cout<<endl;
}
int main()
{
  const int aCount=8,bCount=8,cCount=20;
  int aArray[aCount]={1,2,3,4,5,6,7,8};
  double bArray[bCount]={1.1,2.2,3.3,4.4,5.5,6.6,7.7,8.8};
  char cArray[cCount]="Welcome to see you!";
  cout<<"a Array contains:"<<endl;
  outputArray(aArray,aCount);//調用模闆函數
  
  cout<<"b Array contains:"<<endl;
  outputArray(bArray,bCount);//調用模闆函數
  cout<<"c Array contains:"<<endl;
  outputArray(cArray,cCount);//調用模闆函數
}      

由上面的執行個體可以看出,模闆函數與重載是密切相關的,從函數模闆産生的相關函數都是同名的,編譯器用重載的解決方法調用相應的函數。另外函數模闆本身也是可以用多種方式重載。

2)類模闆

使用類模闆使用者可以為類聲明一種模式,使得類中的某些資料成員、某些成員函數的參數、某些成員函數的傳回值能取任意類型(包括系統預定義的和使用者自定義的)。

類模闆則是對不同類的公共性質的抽象,是以類模闆是屬于更高層次的抽象。由于類模闆需要一種或多種類型參數,是以類模闆也常常稱為參數化類。

文法形式:

template <模闆參數表>

class 類名

{類成員聲明}

其中類成員聲明的方法與普通類的定義幾乎相同,隻是在它的各個成員中通常要用到模闆的類型參數T。

如果需要在類模闆以外定義其成員函數,則要采用以下的形式:

template <模闆參數表>

類型名 類名<T> ::函數名(參數表)

“模闆參數表”由用逗号分隔的若幹類型辨別符或常量表達式構成,其内容包括:

a、class(或typename)辨別符,指明可以接受一個類型參數。

b、類型說明符 辨別符,指明可以接受一個由“類型說明符”所規定的常量作為參數。

當“模闆參數表”同時包含上述多項内容時,各項内容以逗号分隔。應該注意的是,模闆類的成員函數必須是函數模闆。

一個類模闆聲明自身不産生代碼,它說明了類的一個家族。隻有當被其他代碼引用時,模闆才根據引用的需要産生代碼。

使用一個模闆類來建立對象時,應按如下形式聲明:

模闆<模闆參數表> 對象名1,...,對象名n;

通過把參數綁定到形式參數可以建立具體的類,這稱為模闆的執行個體化-生成具體的類。

系統會根據指定的參數類型和常量值生成一個類,然後建立該類的對象。也就是說,對模闆進行執行個體化生成類,再對類執行個體化便生成了對象。

例子:類模闆應用舉例

#include<iostream>
#include<cstdlib>
using namespace std;
struct Student      //結構體
Student {  
int id;//學号
 float gpa;//平均分
};
template <class T>  //類模闆:實作對任意類型資料進行存取
class Store {
private:  T item; //item用于存放任意類型的資料  
int haveValue;//haveValue标記item是否已被存入内容
public:  Store(void);//預設形式(無形參)的構造函數  
T GetElem(void);//提取資料函數  
void PutElem(T x);//存入資料函數
};
//以下實作各成員函數 //注意:模闆類的成員函數,若在類外實作,則必須是模闆函數
template <class T>   //預設形式構造函數的實作
Store<T>::Store(void) :haveValue(0) {}
template <class T>  //提取資料函數的實作
T Store<T>::GetElem(void)
{  if (haveValue == 0)//如果試圖提取為初始化的資料,則終止程式
 {  
 cout << "No item present!" << endl;
  exit(1);//使程式完全退出,傳回到作業系統,參數可用來表示程式終止的原因,可以被作業系統接收
 }  return item;//傳回item中存放的資料 }
template <class T>  //存入資料函數的實作
void Store<T>::PutElem(T x)
{  haveValue++;//将haveValue置為TRUE,表示item中已存入數值
 item = x;//将x值存入item
}
int main()
{  
Student g = { 1000, 23 };//聲明Student結構體變量的同時賦以初值  
Store<int> S1,S2;//聲明兩個Store<int>類對象,其中資料成員item為int類型  
Store<Student> S3;//聲明Store<Student>類對象S3,其中資料成員item為Student類型  
Store<double> D;//聲明Store<double>類對象D,其中資料成員item為double類型
 S1.PutElem(3);//像對象S1中存入資料  
S2.PutElem(-7);  
cout << S1.GetElem() << " " << S2.GetElem() << endl;
 S3.PutElem(g);  
cout << "The student is" << S3.GetElem().id << endl;
 cout << "Retrieving object D";  
cout << D.GetElem() << endl;
 getchar();
 return 0; }