1、函數模板:可以用來創建一個通用功能的函數,以支持多種不同形參,進一步簡化重載函數的函數體設計。
聲明方法:template<typename 標識符> 函數聲明
求絕對值的模板
#include<iostream> ……編譯器從調用abs函數時實參的類型,推導出函數模板的類型參數。
using namespace std; ……如該題從調用abs(int)推導出模板中類型參數T為int型。
template<typename T> ……當類型參數的含義確定后,編譯器將函數模板為樣板,生成一個函數:
T abs(T x) …… int abs(int x)
{ return x<0?-x:x; } ……{return x<0?-x:x;}
int main()
{ int n=-5;
double d=-5.5;
cout<<abs(n)<<endl;
cout<<abs(d)<<endl;
}//運行結果:5 5.5
2、類模板:使用類模板用戶可以為類聲明一種模式,使得類中的某些數據成員、某些成員函數的參數、某些成員函數的返回值,能取任意類型(包括基本類型和自定義類型)。
類模板聲明:template <模板參數表> class 類名 {類成員聲明}
如果需要在類模板之外定義其成員函數,則要采用template <模板參數表> 類型名 類名<T>::函數名(參數表)
#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中存入數據(初始化對象S1)
S2.PutElem(-7); //向對象S2中存入數據(初始化對象S2)
cout<<S1.GetElem()<<" "<<S2.GetElem()<<endl; //輸出對象S1和S2的數據成員
S3.PutElem(g); //向對象D中存入數據(初始化對象D)
cout <<"The student id is "<<S3.GetElem().id << endl; //輸出對象S3的數據成員
cout << "Retrieving object D " ;
cout << D.GetElem() << endl; //輸出對象D的數據成員
// 由於D未經初始化,在執行函數D.GetElement()過程中導致程序終止
}
3、數組類--屬於直接訪問群體
群體的概念:有多個數據元素組成的集合體。群體可以分為兩大類:線性群體和非線性群體。線性群體中的元素按位置排列有序,可以區分為第一個元素、第二個元素等。其元素與其位置的關系是對應的,在線性群體中按照訪問方法不同,我們可以將其分為直接訪問、順序訪問和索引訪問。非線性群體不用位置順序來標識元素。
靜態數組:是具有固定元素個數的群體,其中元素可以通過下標訪問,其大小在編譯時就已經確定,在運行過程中無法修改;
動態數組:有一系列位置連續的任意數量的相同類型的元素組成,其元素個數在程序運行時改變。
動態數組類的模板:Array類模板
//Array.h
#ifndef ARRAY_CLASS
#define ARRAY_CLASS
#include <iostream>
#include <cstdlib>
using namespace std;
#ifndef NULL
const int NULL = 0;
#endif // NULL
//錯誤類型集合,共有三種類型的錯誤:數組大小錯誤、內存分配錯誤和下標越界
enum ErrorType
{invalidArraySize, memoryAllocationError, indexOutOfRange};
//錯誤信息
char *errorMsg[] =
{
"Invalid array size", "Memory allocation error",
"Invalid index: "
};
//數組類模板聲明
template <class T>
class Array
{
private:
T* alist; //T類型指針,用於存放動態分配的數組內存首地址
int size; //數組大小(元素個數)
void Error(ErrorType error,int badIndex=0) const; // 錯誤處理函數
public:
Array(int sz = 50); //構造函數
Array(const Array<T>& A); //拷貝構造函數
~Array(void); //析構函數
Array<T>& operator= (const Array<T>& rhs); //重載"="使數組對象可以整體賦值
T& operator[](int i); //重載"[]",使Array對象可以起到C++普通數組的作用
operator T* (void) const; //重載T*,使Array對象可以起到C++普通數組的作用
int ListSize(void) const; // 取數組的大小
void Resize(int sz); // 修改數組的大小
};
//以下為類成員函數的定義
//模扳函數Error實現輸出錯誤信息的功能
template <class T>
void Array<T>::Error(ErrorType error, int badIndex) const
{
cout << errorMsg[error]; //根據錯誤類型,輸出相應的錯誤信息
if (error == indexOutOfRange)
cout << badIndex; //如果是下標越界錯,輸出錯誤的下標
cout << endl;
exit(1);
}
//構造函數
template <class T>
Array<T>::Array(int sz)
{
if (sz <= 0) //sz為數組大小(元素個數),若小於0,則輸出錯誤信息
Error(invalidArraySize);
size = sz; // 將元素個數賦值給變量size
alist = new T[size]; //動態分配size個T類型的元素空間
if (alist == NULL) //如果分配內存不成功,輸出錯誤信息
Error(memoryAllocationError);
}
// 析構函數
template <class T>
Array<T>::~Array(void)
{ delete [] alist; }
// 拷貝構造函數
template <class T>
Array<T>::Array(const Array<T>& X)
{
//從對象X取得數組大小,並賦值給當前對象的成員
int n = X.size;
size = n;
//為對象申請內存並進行出錯檢查
alist = new T[n]; // 動態分配n個T類型的元素空間
if (alist == NULL) //如果分配內存不成功,輸出錯誤信息
Error(memoryAllocationError);
// 從對象X復制數組元素到本對象
T* srcptr = X.alist; // X.alist是對象X的數組首地址
T* destptr = alist; // alist是本對象中的數組首地址
while (n--) // 逐個復制數組元素
*destptr++ = *srcptr++;
}
// 重載"="運算符,將對象rhs賦值給本對象。實現對象之間的整體賦值
template <class T>
Array<T>& Array<T>::operator= (const Array<T>& rhs)
{
int n = rhs.size; // 取rhs的數組大小
//如果本對象中數組大小與rhs不同,則刪除數組原有內存,然后重新分配
if (size != n)
{
delete [] alist; // 刪除數組原有內存
alist = new T[n]; // 重新分配n個元素的內存
if (alist == NULL) //如果分配內存不成功,輸出錯誤信息
Error(memoryAllocationError);
size = n; //記錄本對象的數組大小
}
// 從rhs向本對象復制元素
T* destptr = alist;
T* srcptr = rhs.alist;
while (n--)
*destptr++ = *srcptr++;
return *this; // 返回當前對象的引用
}
// 重載下標運算符,實現與普通數組一樣通過下標訪問元素,並且具有越界檢查功能
template <class T>
T& Array<T>::operator[] (int n)
{
if (n < 0 || n > size-1) // 檢查下標是否越界
Error(indexOutOfRange,n);
return alist[n]; // 返回下標為n的數組元素
}
//重載指針轉換運算符,將Array類的對象名轉換為T類型的指針,
//指向當前對象中的私有數組。
//因而可以象使用普通數組首地址一樣使用Array類的對象名
template <class T>
Array<T>::operator T* (void) const
{
return alist; // 返回當前對象中私有數組的首地址
}
//取當前數組的大小
template <class T>
int Array<T>::ListSize(void) const
{
return size;
}
// 將數組大小修改為sz
template <class T>
void Array<T>::Resize(int sz)
{
if (sz <= 0) // 檢查是否sz<= 0
Error(invalidArraySize);
if (sz == size) // 如果指定的大小與原有大小一樣,什么也不做
return;
T* newlist = new T[sz]; // 申請新的數組內存
if (newlist == NULL) // 測試申請內存是否申請成功
Error(memoryAllocationError);
int n = (sz <= size) ? sz : size; // 將sz與size中較小的一個賦值給n
// 將原有數組中前n個元素復制到新數組中
T* srcptr = alist; // 原數組alist的首地址
T* destptr = newlist; // 新數組newlist的首地址
while (n--) // 復制數組元素
*destptr++ = *srcptr++;
delete[] alist; // 刪除原數組
alist = newlist; // 使alist 指向新數組
size = sz; // 更新size
}
#endif // ARRAY_CLASS
Array類模板的應用:求范圍2~N中的質數,N在程序運行時由鍵盤輸入 。
#include <iostream>
#include <iomanip>
#include "Array.h"
using namespace std;
int main()
{ Array<int> A(10);
int n;
int primecount = 0, i, j;
cout << "Enter a value >= 2 as upper limit for prime numbers: ";
cin >> n;
A[primecount++] = 2; // 2是一個質數,[]下標運算
for(i = 3; i < n; i++)
{ if (primecount == A.ListSize()) A.Resize(primecount + 10);
if (i % 2 == 0) continue;
j = 3;
while (j <= i/2 && i % j != 0) j += 2;
if (j > i/2) A[primecount++] = i;
}
for (i = 0; i < primecount; i++)
{ cout << setw(5) << A[i];
if ((i+1) % 10 == 0) cout << endl;
}
cout << endl;
}
鏈表、棧和隊列等屬於數據結構中已經詳細說明的內容,在此省略。
4、幾種簡單的內部排序的算法(插入、選擇和交換)
排序的過程主要完成的動作有兩個:比較大小和調整元素在序列中的位置。關於數據元素,在排序過程中需要一個標識符,也就是排序關鍵值,可以標識一個數據元素,也就是數據元素中某個數據項的值。
插入排序:每一步將一個待定排序元素按期關鍵字值的大小插入到已排序列的適當位置上,知道待排元素插入完畢止。直接插入排序函數模板
template <class T>
void InsertionSort(T A[], int n)
{ int i, j;
T temp;
for (i = 1; i < n; i++)
{ j = i;
temp = A[i];
while (j > 0 && temp < A[j-1])
{ A[j] = A[j-1];
j--;
}
A[j] = temp;
}
}
選擇排序:每次排序從待排序的序列中選擇一個關鍵字最小或者最大的元素,順序排在已排序列的最后,直至全部完成。直接選擇的函數模板
template <class T>
void Swap (T &x, T &y)
{ T temp;
temp = x;
x = y;
y = temp;
}
template <class T>
void SelectionSort(T A[], int n)
{ int smallIndex;
int i, j;
for (i = 0; i < n-1; i++)
{ smallIndex = i;
for (j = i+1; j < n; j++)
if (A[j] < A[smallIndex]) smallIndex = j;
Swap(A[i], A[smallIndex]);
}
}
交換排序:兩兩比較待排序序列中的元素,並交換不滿足順序要求的各對元素,知道全部滿足順序要求為止。學習了最簡單的起泡排序。對具有n個元素的序列按升序進行起泡排序的步驟:
首先將第一個元素與第二個元素進行比較,若為逆序,則將兩元素交換。然后比較第二、第三個元素,依次類推,直到第n-1和第n個元素進行了比較和交換。此過程稱為第一趟起泡排序。經過第一趟,最大的元素便被交換到第n個位置。
對前n-1個元素進行第二趟起泡排序,將其中最大元素交換到第n-1個位置。
如此繼續,直到某一趟排序未發生任何交換時,排序完畢。對n個元素的序列,起泡排序最多需要進行n-1趟。
起泡法的函數模板
template <class T>
void Swap (T &x, T &y)
{ T temp;
temp = x;
x = y;
y = temp;
}
template <class T>
void BubbleSort(T A[],int n)
{int i,j;
int lastExchangeIndex;
i = n-1;
while (i > 0)
{ lastExchangeIndex = 0;
for (j = 0; j < i; j++)
if (A[j+1] < A[j])
{ Swap(A[j],A[j+1]);
lastExchangeIndex=j;
}
i=lastExchangeIndex;
}
}