在前面的文章中,我們曾自訂了一個迭代器類line_iterator,不過它並不具有通用性。現在藉助於模板,我們來定義一個模板化的迭代器。仍然延續線性尋找的思路,首先將SimpleFind()函數改寫成函數模板tFind(),代碼如下所示:
template<typename T>T* tFind(T* start, T* end, T val){ while(start != end && *start != val) ++start; return *start == val ? start : (T*)0;}
然而,tFind()只能接受內建指標類型,通用性仍然受限。為了擴充tFind()的適用範圍,進一步將tFind()改寫為:
template<typename Iterator, typename T>Iterator tFind(Iterator start, Iterator end, const T& val){ while(start != end && *start != val) ++start; return start != end ? start : (Iterator)0;}
在tFind()函數模板中,除資料類型T之外,我們還引入了一個Iterator類型。當然Iterator是支援T*的,但它的範圍遠比內建指標來的廣泛。實際上,只要Iterator能夠支援tFind()內部調用的operator!=,operator++等操作符方法就成。之前我們自訂的line_iterator不就是這樣做的嗎?(如果你不知道line_iterator,建議你先大致看看之前的那篇文章)
現在,提出一個新的要求:對一個單鏈表資料結構對象進行線性尋找。我們知道,單鏈表中的各個節點是利用next指標來串聯的,因此對鏈表上某節點node的指標做++操作時,不能簡單的指標加1,而是this->next。如何?這樣的行為?操作符重載正符合我們的要求。
但是,我們卻不能對內建指標node*類型重載operator++,因為那樣做將會改變operator++的語義,C++也不會允許你那樣做。但如果我們將operator++放入自訂class中,成為成員函數的角色,那就沒有什麼特別限制了。也就是說,我們可以對class的成員操作符operator++進行重載。而這裡所說的class,其實就是iterator class。
好,下面就來實現上述想法。首先實現單鏈表,比較容易,只要定義好節點的struct並在I/O操作時按鏈表方式來實現:
// list: roll_listtypedef struct Student{ int num; char name[10]; bool check; struct Student* next;}Student,*roll_list;
我們定義一個學生結構體(Student),其中包含學號、姓名、是否簽到三個資料域,另外還有一個next指標,指向單鏈表的下一個節點。同時又將Student*類型重新命名為roll_list,表示點名單。
在主函數中,對鏈表的操作大概是這樣的:(不要迷惑這些代碼擺放次序,我會在文章的末尾給出完整的範例程式碼)
int num; char ch; roll_list tmp = NULL; while(cin>>num && num != 0) { Student* pStu = new Student; pStu->num = num;cin.get(ch); cin.getline(pStu->name,sizeof(pStu->name)); pStu->check = false; pStu->next = tmp; tmp = pStu; }
點名單的tmp是頭指標,如果一個學生都沒有,則tmp = NULL。在輸入學生資料時,採用了頭插入法。此外,判斷輸入結束的標誌是輸入的學號為0時。
這樣我們就成功構建了單鏈表,正如前面所說,直接調用tFind(tmp,null,n)肯定不行,因為tFind()內部要對指標做++操作,而預設的指標後移在這裡行不通。接下來,就來看看迭代器是怎樣力挽狂瀾的。其實如果你比較熟悉智能指標,你會發現迭代器的思想與智能指標太神似,都是對內建指標的封裝,然後實現必要的介面,讓使用者誤以為它就是內建指標。如果你看過設計模式,大概知道這就是代理模式。言歸正傳,我們來定義這個迭代器:
// list_iteratortemplate<typename T>class list_iterator{ T* m_ptr;public: list_iterator():m_ptr(NULL){} list_iterator(T* ptr):m_ptr(ptr){} T& operator*()const { return *m_ptr; } T* operator->()const { return m_ptr; } bool operator==(const list_iterator& rhs) { return m_ptr == rhs.m_ptr; } bool operator!=(const list_iterator& rhs) { return !(m_ptr == rhs.m_ptr); } list_iterator& operator++() { m_ptr = m_ptr->next; return *this; } list_iterator operator++(int) { list_iterator tmp = *this; m_ptr = m_ptr->next; return tmp; }};
這裡將list_iterator寫成了模板形式,如此一來,它不僅僅支援例子中的Student鏈表,還支援任何其它含有next指標的鏈表,從而提高了代碼的通用性。前面說到了,list_iterator是對內建指標T* m_ptr的封裝,外界使用list_iterator就好象使用T*一樣,因為list_iterator的操作符方法使它完全可以以假亂真。現在考慮tFind的調用:
tFind(list_iterator<Student>(tmp),list_iterator<Student>(), num)
傳給tFind()的參數包括兩個list_iterator<Student>臨時對象以及一個int類型的num,在tFind()的內部調用的operator!=、operator++等操作,list_iterator完全可以勝任,因為我們給它定義了operator++等方法。
下面是樣本程式的運行結果:
8 馬克思10 瑪麗12 米奇13 湯姆20 吉瑞0Input a Student Number to Search:10Find Student:學號:00010 姓名:瑪麗 出席情況:還未簽到
樣本程式的全部代碼如下:
/* tFind.cpp * created: btwsmile * date: 2011-12-18 * remarks: * * input8 馬克思10 瑪麗12 米奇13 湯姆20 吉瑞0 */#include<iostream>#include<iomanip>using namespace std;// list: roll_listtypedef struct Student{ int num; char name[10]; bool check; struct Student* next;}Student,*roll_list;// operaotr==(Student, int)bool operator==(const Student& lhs, int rhs){ return lhs.num == rhs;}bool operator!=(const Student& lhs, int rhs){ return !(lhs.num == rhs);}// list_iteratortemplate<typename T>class list_iterator{ T* m_ptr;public: list_iterator():m_ptr(NULL){} list_iterator(T* ptr):m_ptr(ptr){} T& operator*()const { return *m_ptr; } T* operator->()const { return m_ptr; } bool operator==(const list_iterator& rhs) { return m_ptr == rhs.m_ptr; } bool operator!=(const list_iterator& rhs) { return !(m_ptr == rhs.m_ptr); } list_iterator& operator++() { m_ptr = m_ptr->next; return *this; } list_iterator operator++(int) { list_iterator tmp = *this; m_ptr = m_ptr->next; return tmp; }};// function template: tFind/*template<typename T>T* tFind(T* start, T* end, T val){ while(start != end && *start != val) ++start; return *start == val ? start : (T*)0;}*/template<typename Iterator, typename T>Iterator tFind(Iterator start, Iterator end, const T& val){ while(start != end && *start != val) ++start; return start != end ? start : (Iterator)0;}// main functionint main(){ int num; char ch; roll_list tmp = NULL; while(cin>>num && num != 0) { Student* pStu = new Student; pStu->num = num;cin.get(ch); cin.getline(pStu->name,sizeof(pStu->name)); pStu->check = false; pStu->next = tmp; tmp = pStu; } cout<<"Input a Student Number to Search:"; cin>>num; list_iterator<Student> t; if((t = tFind(list_iterator<Student>(tmp),list_iterator<Student>(), num)) != NULL) { cout<<"Find Student:\n"; cout<<"學號:"<<setw(5)<<setfill('0')<<t->num<<"\t姓名:"<<t->name<<"\t出席情況:"<<(t->check ? "已經簽到" : "還未簽到"); } else cout<<"No Such Student in Roll List"; cout<<endl<<endl; system("pause"); return 0;}