- 泛型程序設計:
在面向對象的程序設計中,允許將類中成員的類型設爲一個可變的參數,使多個類變成一個類,這種程序設計機制稱爲泛型程序設計。泛型程序設計可以以獨立於任何特定類型的方式編寫代碼,使用泛型程序時,必須提供具體的所操作的類型或值。 - 函數模板:
一組重載函數僅僅是參數的類型不一樣,程序的邏輯完全一樣,代碼大致相同,類型不同,可以寫成一個函數,稱爲函數模板,減少代碼量。是實現類型的參數化(泛型化),即把函數中某些形式參數的類型設計成可變的參數,稱爲模板參數。編譯器根據函數實際參數的類型確定模板參數的值,生成不同的模板參數。
定義形式:
注意:通常每個模板的形式參數都要在函數的形式參數表中至少出現一次,這樣編譯器才能通過函數調用確定模板參數的值。如果有些模板參數在函數的形式參數表中沒有出現,編譯器就無法推斷出模板實際參數的類型,可以通過顯式指定模板實參來解決。template<class T> T max(T a,T b){ return a > b ? a : b ; }
template<class T1, class T2, class T3> T3 calc(T1 x, T2 y){ return x+y; } //調用時 calc<int, char, char>(5, 'a'); //模板實例化 calc<int, char, int>(5, 'a');
類模板:
類中不確定的類型設計成一個模板參數,用模板參數代替類中的某些成員的類型。
定義形式:
類模板可以定義數據成員和成員函數,構造函數和析構函數,也可以重載運算符。template<模板形式參數表> class 類名(...);
成員函數的參數類型或返回值類型也可以是模板的形式參數,成員函數的某些局部變量的類型也可以是模板的形式參數。
- 類模板成員函數定義形式:
必須以template開頭,後接模板形式參數表。
必須用作用域限定符 :: 說明其是哪個類的成員函數。
類名必須包含其模板形式參數。// 可指定下標範圍的、支持任意類型的、安全的動態數組的定義 template <class T> // 模板參數T是數組元素的類型 class Array { int low; int high; T *storage; public: //根據low和high爲數組分配空間。分配成功,返回值爲true,否則返回值爲false Array(int lh = 0, int rh = 0):low(lh),high(rh) { storage = new T [high - low + 1]; } //複製構造函數 Array(const Array &arr); //賦值運算符重載函數 Array &operator=(const Array & a); //下標運算符重載函數 T & operator[](int index); //回收數組空間 ~Array() { if (storage) delete [] storage; } }; // 類模板Array的成員函數的實現 // 複製構造函數 template <class T> Array<T>::Array(const Array<T> &arr) //函數名爲Array { low =arr.low; high = arr.high; storage = new T [high - low + 1]; for (int i = 0; i < high -low + 1;++i) storage[i] = arr.storage[i]; } // 賦值運算符重載函數 template<class T> Array<T> &Array<T>::operator=(const Array<T> &other ) //函數名爲operator= { if (this == &other) return *this; //防止自己複製自己 delete [] storage; //歸還空間 low = other.low; high = other.high; storage = new T[high - low + 1]; //根據新的數組大小重新申請空間 for (int i=0; i <= high - low; ++i) //複製數組元素 storage[i] = other.storage[i]; return *this; } // 下標運算符重載函數 template<class T> T &Array<T>::operator[](int index) //函數名爲operator[] { if(index < low || index > high) { cout<< "下標越界"; exit(-1); } return storage[index - low]; }
- 類模板實例化:
函數模板的實例化通常由編譯器自動完成,編譯器根據函數調用時的實際參數類型推斷出模板參數的值,將模板參數的值代入函數模板生成一個真正可執行的模板函數。
對於類模板,編譯器無法根據對象定義的過程確定模板參數的類型,因而需要用戶明確指出模板形式參數的值,定義格式:
編譯器首先將模板的實際參數值代入類模板,生成一個可真正使用的類,然後定義這個類的對象。類模板名<模板實際參數表> 對象表; Array<int> array1(20,30);
- 非類型參數和參數的默認值:
類型參數:模板參數都是用來表示一個尚未明確的類型,這些參數是類型參數。
非類型參數:模板的形式參數不一定都是類型,可以是整數型或實數型。
非類型模板實例化,非類型參數將用一個常量表達式作爲實際參數。
模板參數和普通的函數參數一樣,也可以指定默認值,可在類模板定義時指定默認值。// 將數組的下標範圍作爲模板的非類型參數 template <class T, int low, int high> class Array{ T storage[high - low + 1]; public: T & operator[](int index) ; //下標運算符重載函數 }; template <class T, int low, int high> T & Array<T, low, high>::operator[](int index) { if (index < low || index > high) { cout << "下標越界"; exit(-1); } return storage[index - low]; } Array<int, 10, 20>array;
這樣就可以不指定模板的實參template<class T = int> class Array{...};
Array<> array
- 類模板友元:(兩種友元)
普通友元:聲明某個普通的類或全局函數爲所定義的類模板的友元。
定義形式:
意味類B和函數f是類模板A所有的實例的友元,B的所有的成員函數和全局函數f可以訪問類模板A的所有實例的私有成員。template<class type> class A{ friend class B; friend void f(); ..; };
模板的特定實例的友元:聲明某個類模板或函數模板的特定實例是所定義類模板的友元。
將類模板B和函數模板f對應於模板參數爲int時的那個實例,作爲類模板A的所有實例的友元(如下)
將使用某一模板實參的類模板B和函數模板f的實例是使用同一模板參數的類模板A的特定實例的友元(如下)template<class T>class B; //類模板B的聲明,B有一個模板參數 template<class T>void f(const T &); //函數模板f的聲明,f有一個模板參數 template<class type> class A{ friend class B <int>; friend void f(const int &); ... };
當聲明類模板B和函數模板f爲類模板A的友元時,編譯器必須知道有這樣一個類模板和函數模板的存在,並且知道類模板B和函數模板f的原型,因此,必須在友元聲明之前先聲明B和f的存在。template<class T>class B; template<class T>void f(const T &); template<class type> class A{ friend class B <type>; friend void f(const type &); ... };
// 類模板的友元實例:重載輸出運算符 template<class type> ostream &operator<<(ostream &os, const Array<type> &obj) { os << endl; for (int i=0; i < obj.high - obj.low + 1; ++i) os << obj.storage[i] << '\t'; return os; } // 類模板的友元實例:重載輸出運算符 template <class T> class Array; //類模板Array的聲明 template<class T> ostream &operator<<(ostream &os, const Array<T>&obj); //輸出重載聲明 //聲明瞭函數模板operator<<的一個實參爲T的實例是類模板Array的實參爲T的實例的友元。 template <class T> class Array { friend ostream &operator<<(ostream &, const Array<T> &); private: int low; int high; T *storage; public: //根據low和high爲數組分配空間。分配成功,返回值爲true,否則返回值爲false Array(int lh = 0, int rh = 0):low(lh),high(rh) { storage = new T [high - low + 1]; } //複製構造函數 Array(const Array &arr); //賦值運算符重載函數 Array &operator=(const Array & a); //下標運算符重載函數 T & operator[](int index) {return storage[index - low];} //回收數組空間 ~Array() {delete [] storage; } }; 對於Array的任何一個實例: Array <int> array(10, 20); 可以用 cout << array; 輸出
- 類模板應用:
- 單鏈表類及結點類:
定義兩個獨立的類,由於鏈表操作經常需要訪問結點的數據成員,因此,將同一模板參數的鏈表類設置爲結點類的友元。
實現對任何類型的單鏈表的操作// 單鏈表模板類中的結點類和鏈表類的定義 template <class elemType> class linkList; template <class T> ostream &operator<<(ostream &, const linkList<T> &); template <class elemType> class Node ; template <class elemType> class Node { friend class linkList<elemType>; friend ostream &operator<<( ostream &, const linkList<elemType> &); private: elemType data; Node <elemType> *next; public: Node(const elemType &x, Node <elemType> *N = NULL) { data = x; next = N;} Node( ):next(NULL) {} ~Node() {} }; template <class elemType> class linkList { friend ostream &operator<<( ostream &, const linkList<elemType> &); protected: Node <elemType> *head; void makeEmpty(); // 清空鏈表 public: linkList() { head = new Node<elemType>; } ~linkList() {makeEmpty(); delete head;} void create(const elemType &flag); }; // 單鏈表類的成員函數及友元函數的定義 template <class elemType> void linkList<elemType>::makeEmpty() { Node <elemType> *p = head->next, *q; head->next=NULL; while (p != NULL) { q=p->next; delete p; p=q;} } template <class elemType> void linkList<elemType>::create(const elemType &flag) { elemType tmp; Node <elemType> *p, *q = head; cout << "請輸入鏈表數據," << flag << "表示結束" << endl; while (true) { cin >> tmp; if (tmp == flag) break; p = new Node<elemType>(tmp); q->next = p; q = p; } } template <class T> ostream &operator<<(ostream &os, const linkList<T> &obj) { Node <T> *q = obj.head->next; os << endl; while (q != NULL){ os << q->data; q = q->next; } return os; }
linkList <int> intList; //實例化定義一個整型的單鏈表 intList.create(0); //輸入鏈表中的元素值,直到輸入0爲止。 cout << intList; //輸出鏈表的所有元素
- 棧的類模板:
類模板可以作爲繼承關係的基類,類模板的繼承和普通的繼承方法類似,只是在涉及基類時,都必須帶上模板參數。
棧是特殊的線性表,插入和刪除只能在表的一端進行,允許插入和刪除的一端稱爲棧頂,另一端爲棧底,常用操作爲進棧(push)和出棧(pop)。
棧在單鏈表的基礎上增加兩個操作push和pop,push在單鏈表的表頭插入一個元素,pop是刪除單鏈表的表頭元素。
爲使Stack能訪問Node的數據成員,必須將Stack設爲Node的友元。// 在鏈表類的基礎上派生一個棧類 template <class elemType> class Stack:public linkList<elemType> { public: void push(const elemType &data) { Node <elemType> *p = new Node<elemType>(data); p->next = head->next; head->next = p; } bool pop(elemType &data) //棧爲空時返回false,出棧的值在data中 { Node <elemType> *p = head->next; if ( p == NULL) return false; head->next = p->next; data = p->data; delete p; return true; } };
- 編程規範:
繼承和組合提供一種重用對象代碼的方法
模板提供了重用源代碼的方法,模板通過將類型作爲參數,將處理不同類型數據的一組函數或類綜合成一個函數或一個類,即函數模板和類模板,減輕代碼量。
類模板的形式參數可以是普通類型,稱爲非類型參數,非類型參數的實際參數必須是編譯時的常量。