Saturday, February 18, 2012

templates(模板)

templates採通用格式的方法,在C++語言中可大量減少撰寫重複的程式碼,分為兩種形式:

  • function templates
  • class templates

Function Templates

類似於overload functions

  • overload functions用於相似或是相同的情況
  • function templates則用於完全相同而只有資料型態差異的情況下
definition(出現在該function前面):
  • template< typename T >
  • template< class ElementType >
在<>符號中間的為template parameters,超過一個用逗號分開,型態有class或typename。

#include <iostream>
using namespace std;

// function template
template< typename T>
void printArray(const T * const array, int size){
 for(int i=0 ; i<size ; i++){
  cout << array[i] << " ";
 }
 cout << endl;
}

int main(){

 int a[4] = {1,2,4,3};
 char b[3] = {'b', 'c', 'a'};
 double c[5] = {1.23, 12.3, 0.12, 1.233, 0.11};

 printArray(a, 4);
 printArray(b, 3);
 printArray(c, 5);

 return 0;
}


範例中,當int a[]傳進來的時候T就會變成int,char進來則是char,以此類推。

"The compiler uses its overload resolution capabilities to find a definition of function printArray the best matched the function call."
compiler使用類似於overload的解法處理printArray中的T,會自動找尋最合適的function call。
如此可以避免寫重複的程式碼,產生一些「通用」的function。

注意:

  • 若是自訂的資料型態,要記得有沒有先overload該型態的operators(for example, ==, >=, ...)
  • 同一個template definition裡頭names of template parameters不能重複;不同template definition則可以重複
  • template的處理在compile time

Class Templates

另一種template為class templates,加在class definition的前面:欲達到的效果和function templates一樣,希望可以產生一個「通用」的方式,避免撰寫重複的程式碼。


Stack.h
#include <iostream>

using namespace std;

template<typename T>
class Stack{
 public:
  Stack(int = 10); // default constructor
  ~Stack(){ // destructor
   delete [] stackPtr;
  }
  bool push(const T &);
  bool pop(T &);

  bool isEmpty() const{
   return top == - 1;
  }
  bool isFull() const{
   return top == size - 1;
  }

 private:
  int size;
  int top;
  T *stackPtr;
};

template<typename T>
Stack< T >::Stack(int s):
 size(s>0 ? s : 10),
 top(-1),
 stackPtr( new T[size] ){
}

template<typename T>
bool Stack< T >::push(const T &pushValue){
 if(!isFull()){
  stackPtr[++top] = pushValue;
  return true;
 }
 return false;
}

template<typename T>
bool Stack<T>::pop(T &popValue){
 if(!isEmpty()){
  popValue = stackPtr[top--];
  return true;
 }
 return false;
}

main.cpp
#include <iostream>
#include "Stack.h"
using namespace std;

int main(){

 /* int */
 Stack<int> intStack(5);
 int intValue = 1;

 // push
 while(intStack.push(intValue)){
  cout << intValue << " ";
  intValue += 2;
 }
 cout << endl;

 // pop
 while(intStack.pop(intValue)){
  cout << intValue << " ";
 }
 cout << endl;


 /* double */
 Stack<double> doubleStack(6);
 double doubleValue = 1.0;

 // push
 while(doubleStack.push(doubleValue)){
  cout << doubleValue << " ";
  doubleValue *= 1.1;
 }
 cout << endl;

 // pop
 while(doubleStack.pop(doubleValue)){
  cout << doubleValue << " ";
 }
 cout << endl;

 return 0;
}

另外,兩件事情值得一提:

其一non-type template parameters,我們可以透過以下的方式使用template parameters達到一般argument的效果,其中template definition:
template<typename T, int elements>
使用:
Stack<int, 13> helloStack;
T對應到int,elements對應到13

其二,default type,當使用時沒有給予資料型態時賦予,則預設資料型態,用以下方法定義:
template<typename T = int>
當我們這麼使用時,T會變成預設的int:
Stack<> scores;