迭代器與traits編程技法

來源:互聯網
上載者:User

2013-06-15 wcdj

迭代器設計思維

(1) 迭代器(iterators)是一種抽象的設計概念,現實程式語言中並沒有直接對應於這個概念的實物。

(2)《設計模式》一書提供有23個design patterns的完整描述,其中iterator模式定義為:提供一種方法,使之能夠依序巡防某個彙總物(容器)所含的各個元素,而又無需暴露該彙總物的內部表述方式。

(3) 不論是泛型思維或STL的實際運用,iterators都扮演著重要的角色。STL的中心思想在於:將資料容器(containers)和演算法(algorithms)分開,彼此獨立設計,最後再以一貼膠著劑將它們撮合在一起。容器和演算法的泛型化,從技術角度看並不困難,C++的class templates和functiontemplates可分別達成目標。如何設計出兩者之間的良好膠著劑才是大難題。以演算法find為例,以下是容器、演算法、迭代器的合作展示:

/**   *  @if maint   *  This is an overload used by find() for the Input Iterator case.   *  @endif  */  template<typename _InputIterator, typename _Tp>    inline _InputIterator    __find(_InputIterator __first, _InputIterator __last,   const _Tp& __val, input_iterator_tag)    {      while (__first != __last && !(*__first == __val))++__first;      return __first;    }

只要給予不同的迭代器,find便能夠對不同的容器進行尋找操作:

// file: 3find.cpp#include <vector>#include <list>#include <deque>#include <algorithm>#include <iostream>using namespace std;int main(){const int arraySize = 7;int ia[arraySize] = {0,1,2,3,4,5,6};vector<int> ivect(ia, ia+arraySize);list<int> ilist(ia, ia+arraySize);deque<int> ideque(ia, ia+arraySize);vector<int>::iterator it1 = find(ivect.begin(), ivect.end(), 4);if (it1 == ivect.end()){cout << "4 not found." << endl;}else{cout << "4 found. " << *it1 << endl;  }list<int>::iterator it2 = find(ilist.begin(), ilist.end(), 6);if (it2 == ilist.end()){cout << "4 not found." << endl;}else{cout << "4 found. " << *it2 << endl;  }deque<int>::iterator it3 = find(ideque.begin(), ideque.end(), 8);if (it3 == ideque.end()){cout << "4 not found." << endl;}else{cout << "4 found. " << *it3 << endl;  }return 0;}/*output:mba:stl gerryyang$ g++ -o 3find 3find.cpp mba:stl gerryyang$ ls3find3find.cppmba:stl gerryyang$ ./3find 4 found. 44 found. 64 not found.*/

(4) 迭代器是一種smart pointer。迭代器是一種行為類似指標的對象,而指標的最常見行為是dereference和member acess,因此迭代器最重要的工作就是對operator*和operater->進行overloading工作。在C++標準庫中auto_ptr是一個用來封裝native pointer的對象,以解決聲名狼藉的memory leak問題,auto_ptr用法和原生指標一模一樣:

#include <iostream>#include <string>#include <memory>// auto_ptr classusing std::cout;using std::endl;using std::string;using std::auto_ptr;void f1(){int *ip = new int(123);// dynamically allocate a new objectcout << "&ip = " << ip << "; *ip = " << *ip << endl;/* code that throws an exception that is not caught inside f1() */delete ip;}void f2(){auto_ptr<int> ap(new int(456));// allocate a new objectcout << "&ap = " << &ap << "; *ap = " << *ap << endl;/* code that throws an exception that is not caught inside f2() */// auto_ptr freed automatically when function ends}void f3(){auto_ptr<string> ap_string(new string("delphiwcdj"));cout << "ap_string = " << *ap_string << endl;*ap_string = "gerry";cout << "ap_string = " << *ap_string << endl;if (ap_string->empty()){cout << "ap_string is empty" << endl;}else{cout << "ap_string is non-empty" << endl;}}int main() {cout << "auto_ptr usage\n" << endl;f1();f2();f3();return 0;}/*g++ -Wall -g -o auto_ptr auto_ptr.cpp output:auto_ptr usage&ip = 0x804b008; *ip = 123&ap = 0xbfa85d70; *ap = 456ap_string = delphiwcdjap_string = gerryap_string is non-empty*/

(5) 為了完成一個針對某種容器而設計的迭代器,我們無可避免地會暴露了太多此容器實現細節,即要設計出某種容器的迭代器,首先必須對此容器的實現細節有非常豐富的瞭解,既然這無可避免,乾脆就把迭代器的開發工作交給容器的設計者好了,如此一來,所有實現細節反而得以封裝起來不被使用者看到。這正是為什麼每一種STL容器都提供有專屬迭代器的緣故。

(6) 迭代器相應型別(associated types)

假設演算法中有必要聲明一個變數,以“迭代器所指對象的型別”為型別如何是好?畢竟C++只支援sizeof(),並未支援typeof(),即便動用RTTI性質中的typeid(),獲得的也只是型別名稱,不能拿來做變數聲明之用。解決辦法是:利用function template的參數推導(argument deducation)機制。

PS: 物件導向編程所依賴的多態性稱為運行時多態性,泛型程式設計所依賴的多態性稱為編譯時間多態性參數式多態性

#include <iostream>using namespace std;template <class I, class T>void func_impl(I iter, T t){    T tmp;// 這裡解決了問題,T就是迭代器所指之物的型別,本例為int/short    // 這裡做原本func()應該做的全部工作    cout << "func_impl: sizeof(tmp)=" << sizeof(tmp) << endl;    cout << "func_impl: t=" << t << endl;}template <class I>inlinevoid func(I iter){    func_impl(iter, *iter);// func的全部工作移交給func_impl}int main(){    int i = 10;    func(&i);    short j = 20;    func(&j);    return 0;}/*output:mba:stl gerryyang$ g++ -o 3find 3find.cpp mba:stl gerryyang$ ./3find func_impl: sizeof(tmp)=4func_impl: t=10func_impl: sizeof(tmp)=2func_impl: t=20*/

#include <iostream>using namespace std;template <typename I, typename T>T func_impl(I iter, T t){T tmp;// 這裡解決了問題,T就是迭代器所指之物的型別,本例為int/short// 這裡做原本func()應該做的全部工作cout << "func_impl: sizeof(tmp)=" << sizeof(tmp) << endl;cout << "func_impl: t=" << t << endl;return t+1;}template <typename I>inlinevoid func(I iter){*iter = func_impl(iter, *iter);// func的全部工作移交給func_impl}int main(){int i = 10;func(&i);cout << "i=" << i << endl;short j = 20;func(&j);cout << "j=" << j << endl;return 0;}/*output:mba:stl gerryyang$ g++ -o 3find 3find.cpp mba:stl gerryyang$ ./3find func_impl: sizeof(tmp)=4func_impl: t=10i=11func_impl: sizeof(tmp)=2func_impl: t=20j=21*/

注意:迭代器相應型別不只是“迭代器所指對象的型別(value type)”一種而已,最常用的相應型別有5種,然而並非任何情況下任何一種都可以利用上述的template參數推導機制來取得。

Traits編程技法

利用function template的參數推導技巧並非全面可用,例如,value type必須用於函數的傳回值就素手無策了,畢竟函數的“template參數推導機制”推而導之的只是參數,無法推導函數的傳回值型別。

參考:

[1] STL源碼剖析,第三章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.