[STL deep dive]迭代器的实现探究2

这里把STL里处理iterator的tag-dispatching + trait class机制提取一点出来并浅析之.
完成了一个非常简易版的迭代器STL原型.完整版请查看(STL-Port)http://www.stlport.org/download.html].

这里只是为了说明:

  • STL如何实现迭代器:
    1. 指定一个空struct作为不同级别迭代器的tag;
    2. trait_class用来提取具体某个class(迭代器class)中的这些域:iterator_category,value_type,difference_type,pointer,reference.
  • STL的每个Container都需要自己定义一个迭代器,并显式地指明s个域,STL目前的版本是需要指明iterator_category,value_type,difference_type,pointer,reference这几个域
  • <algorithm>中针对不同级别的迭代器指定不同的worker函数(利用函数重载机制).

山寨STL的iterator框架

文件列表:

_algorithm.h  
_algorithm_impl.tcc  
_features.h  
_iterator_base.h  
MyString_MyTest_Portable.cc
  • _features.h:
  1 #ifndef MYTEST__FEATURES_H
  2 #define MYTEST__FEATURES_H
  3 # define MYSPACENAME MyTest
  4 # define MYTEST_NAMESPACE_BEGIN namespace MYSPACENAME{
  5 # define MYTEST_NAMESPACE_END }
  6 #endif
  • _iterator_base.h:
  1 /* a toy for investigating impl. of STL-iterator: the so-called `tag-dispatching` & **trait_class**.
  2 * author: WeijianYang<weijyang@foxmail.com>
  3 *
  4 */
  5 #ifndef MYTEST__ITERATOR_BASE_H
  6 #define MYTEST__ITERATOR_BASE_H
  7 #include <cstddef>
  8
  9 #include "_features.h"
 10
 11 MYTEST_NAMESPACE_BEGIN
 12 //tags
 13 //they are all empty struct declaration
 14 //not diff between STL-impl but add the prefix `` :)
 15 struct input_iterator_tag {};
 16 struct output_iterator_tag {};
 17 struct forward_iterator_tag : public input_iterator_tag {};
 18 struct bidirectional_iterator_tag : public forward_iterator_tag {};
 19 struct random_access_iterator_tag : public bidirectional_iterator_tag {};
 20
 21 //`the trait of a class` means the feature of a class.
 22 template <class Iterator>
 23 struct iterator_traits {
 24     typedef typename Iterator::iterator_category iterator_category;
 25     //typedef typename xxxxx aaaaa - `typename` keyword indicates `xxxxx` is a type. And `typedef` indicates     an alias of xxxxx.
 26     typedef typename Iterator::value_type        value_type;
 27     typedef typename Iterator::difference_type   difference_type;
 28     typedef typename Iterator::pointer           pointer;
 29     typedef typename Iterator::reference         reference;
 30 };
 31
 32 //partially specialized for a pointer
 33 template <class Tp>
 34 struct iterator_traits<const Tp*> {
 35     typedef random_access_iterator_tag  iterator_category;
 36     typedef Tp            value_type;
 37     typedef ptrdiff_t     difference_type;
 38     typedef const Tp*     pointer;
 39     typedef const Tp&     reference;
 40 };
 41
 42 template <class Tp>
 43 struct iterator_traits<Tp*> {
 44     typedef random_access_iterator_tag  iterator_category;
 45     typedef Tp                         value_type;
 46     typedef ptrdiff_t                  difference_type;
 47     typedef Tp*                        pointer;
 48     typedef Tp&                        reference;
 49 };
 50
 51 #define DIFF_TYPE(ITER) typename iterator_traits<ITER>::difference_type
 53 //ptr/non-ptr type traits.
 54 struct ptr_type{};
 55 struct non_ptr_type{};
 56
 57 template <typename T>
 58 struct is_pointer_type
 59 {
 60     static non_ptr_type _val(){ return non_ptr_type();}
 61 };
 62
 63 template <typename T>
 64 struct is_pointer_type<T*>
 65 {
 66     static ptr_type _val(){ return ptr_type();}
 67 };
 68
 69
 70 template<class _Tp>
 71 inline random_access_iterator_tag __iterator_category(const _Tp*, const ptr_type& ){
 72     return random_access_iterator_tag();
 73 }
 74
 75 template<class _Tp>
 76 inline typename iterator_traits<_Tp>::iterator_category
 77 __iterator_category(const _Tp &,const non_ptr_type& ){
 78     typedef typename iterator_traits<_Tp>::iterator_category _Cate;
 79     return _Cate();
 80 }
 81
 82 #define ITERATOR_CATEGORY(_IT, _TP) __iterator_category(_IT, is_pointer_type<_TP>::_val())
 83
 84 MYTEST_NAMESPACE_END
 85 #endif

简要说明:
15~50行直接从STL里抄下来的,
53~80主要是简单版的STL的ITERATOR_CATEGORY宏,主要是利用偏特化 + 重载来获取特定某个Iterator对象的iterator_category标签.

  • _algorithm_impl.tcc:
  1 MYTEST_NAMESPACE_BEGIN
  2
  3 //input iterator only support ++, ==, !=
  4 template <class _InputIterator>
  5 DIFF_TYPE(_InputIterator)
  6 __distance(const _InputIterator &start,
  7             const _InputIterator &end,
  8             const input_iterator_tag &){
  9     DIFF_TYPE(_InputIterator) steps = 0;
 10     _InputIterator itr(start);
 11     while(itr != end){
 12         ++itr; ++steps;
 13     }
 14     return steps;
 15 }
 16 template <class _ForwardIterator>
 17 DIFF_TYPE(_ForwardIterator)
 18 __distance(const _ForwardIterator & start,
 19             const _ForwardIterator & end,
 20             const forward_iterator_tag &){
 21
 22     DIFF_TYPE(_ForwardIterator) steps = 0;
 23     _ForwardIterator itr(start);
 24     while(start != end){
 25         ++itr; ++steps;
 26     }
 27     return steps;
 28 }
 29
 30 template <class _RandomAccessIterator>
 31 DIFF_TYPE(_RandomAccessIterator)
 32 __distance(const _RandomAccessIterator & start,
 33             const _RandomAccessIterator & end,
 34             const random_access_iterator_tag &){
 35     return end - start;
 36 }
 37
 38 template <class _Iterator>
 39 DIFF_TYPE(_Iterator) distance(_Iterator start, _Iterator end){
 40     return __distance(start, end, ITERATOR_CATEGORY(start, _Iterator));
 41 }
 42
 43 MYTEST_NAMESPACE_END

简要说明:这里实现了一个distance做例子,实际上STL的distance是直接实现在iterator_base里的,与这个类似,std:copy()或者std::find()都是有一些worker函数,用来针对不同级别的迭代器.反正就类似这样,核心思想是利用参数重载实现编译时绑定.

  • _algorithm.h:
  1 #ifndef MYTEST_ALGOR_H
  2 #define MYTEST_ALGOR_H
  3
  4 #include "_iterator_base.h"
  5
  6 MYTEST_NAMESPACE_BEGIN
  7
  8 template <class _Iterator>
  9 DIFF_TYPE(_Iterator) distance(_Iterator start, _Iterator end);//STL: _iterator_base.h
 10 /*
 11 template <class _InputIterator, class _Tp>
 12 _InputIterator find(_InputIterator start, _InputIterator end, const _Tp& what){ }
 13
 14 template <class _InputIterator, class _Predicate>
 15 _InputIterator find_if(_InputIterator start, _InputIterator end, _Predicate __pred){ }
 16 */
 17 MYTEST_NAMESPACE_END
 18
 19 #include "_algorithm_impl.tcc" //template function must be visible to user.
 20 #endif

对外接口声明.

  • MyString_MyTest_Portable.cc:
  1 #include <cstdio>
  2 #include <cstring>
  3 #include <cassert>
  4 #include <iostream>
  5
  6 #include "_algorithm.h"     //for MyTest::distance()
  7
  8 class MyString{
  9     public:
 10         MyString() : data_(NULL), size_(0){ }
 11         MyString(const char* s);
 12         MyString(const MyString &b) : size_(b.size_), data_(NULL){
 13             //deep copy
 14             if(size_){
 15                 data_ = new char [BufSize(size_)];
 16                 memcpy(data_, b.data_, BufSize(size_));
 17             }
 18         }
 19         ~MyString(){
 20             if(data_){
 21                 delete [] data_;
 22             }
 23         }
 24
 25         MyString& operator=(const MyString &);
 26         bool operator!=(const MyString &);
 27         bool operator==(const MyString &);
 28         MyString operator+(const MyString &);
 29         MyString& operator+=(const MyString &);
 30         char& operator[](const int);
 31
 32
 33
 34         class Iterator{
 35             public:
 36                 typedef typename MyTest::random_access_iterator_tag iterator_category;
 37                 typedef char value_type;
 38                 typedef int difference_type;
 39                 typedef char* pointer;
 40                 typedef char& reference;
 41
 42                 Iterator() : ptr(NULL){ printf("[DBG] Iterator() is called\n"); }
 43                 Iterator& operator++(){
 44                     ptr++;
 45                     return *this;
 46                 }
 47                 Iterator& operator--(){
 48                     ptr--;
 49                     return *this;
 50                 }
 51                 Iterator& operator+(int x){
 52                     ptr += x;
 53                     return *this;
 54                 }
 55                 Iterator& operator-(int x){
 56                     ptr -= x;
 57                     return *this;
 58                 }
 59                 //error: no match for ‘operator-’ (operand types are ‘MyString::Iterator’ and ‘MyString::Ite    rator’)
 60                 //we have to impl the operator-(Iterator)
 61                 difference_type operator-(const Iterator& rhs) const{
 62                     return ptr - rhs.ptr;
 63                 }
 64                 bool operator!=(const Iterator &s){
 65                     return s.ptr != ptr;
 66                 }
 67                 bool operator==(const Iterator &s){
 68                     return !(*this != s);
 69                 }
 70                 //std::find() will use this operator
 71                 bool operator==(value_type c){
 72                     return *ptr == c;
 73                 }
 74                 char& operator*(){
 75                     return *ptr;
 76                 }
 77
 78             private:
 79                 friend MyString;
 80                 Iterator(char *s) :  ptr(s){
 81 //                  printf("[DBG] Iterator(char*) is called\n");
 82                 }
 83             protected:
 84                 char* ptr;
 85         };
 86
 87         class OuputIterator : public Iterator{
 88             public:
 89                 char& operator*(){
 90                     if(ptr == mptr_->end().ptr){
 91                         int offset = mptr_->size_;
 92                         mptr_->ReSizeCopyBuf((mptr_->size_ + 1) * 2);
 93                         ptr = mptr_->data_ + offset;
 94                     }
 95                     return *ptr;
 96                 }
 97                 void print(){
 98                     printf("[DBG] %p\n", ptr);
 99                 }
100             private:
101                 friend MyString;//friend is not inherited
102                 MyString* mptr_;
103                 OuputIterator(MyString* me) : mptr_(me){ }
104                 OuputIterator(char *s) : Iterator(s) { /*printf("[DBG] OuputIterator(char*) is called\n"); */}
105         };
106
107         Iterator begin(){
108             return Iterator(data_);
109         }
110         Iterator end(){
111             return Iterator(data_ + size_);
112         }
113         OuputIterator obegin(){
114             return OuputIterator(data_);
115         }
116         OuputIterator oend(){
117             return OuputIterator(data_ + size_);
118         }
119
120         int size(){ return size_; }
121     private:
122         char* data_;//end with '\0'
123         int size_;
124         int BufSize(const int s) const{ return s + 1; }
125         char* ReSizeBuf(int s){
126 //          std::cout << "[DBG]\n";
127 //          std::cout << s << size_ << std::endl;
128             if(s > size_){
129                 if(data_){ delete [] data_; }
130                 data_ = new char [BufSize(s)];
131             }
132             size_ = s;
133             return data_;
134         }
135         char* ReSizeCopyBuf(int s){
136             if(s > size_){
137                 char* new_data_ = new char [BufSize(s)];
138                 if(data_){
139                     memcpy(new_data_, data_, BufSize(size_));
140                     delete [] data_;
141                 }
142                 data_ = new_data_;
143             }
144             size_ = s;
145             return data_;
146         }
147         friend OuputIterator;
148         friend std::ostream & operator<<(std::ostream &out, const MyString& s);
149 };
150 MyString::MyString(const char* s)
151  : size_(strlen(s)),
152  data_(NULL)
153 {
154     if(size_){
155         data_ = new char [BufSize(size_)];
156         memcpy(data_, s, BufSize(size_));
157     }
158 }
159
160 MyString& MyString::operator=(const MyString &b)
161 {
162     //deep copy
163     //origin data is overwrote
164     if(&b != this){
165         ReSizeBuf(b.size_);
166         memcpy(data_, b.data_, BufSize(size_));
167     }
168     return *this;
169 }
170 bool MyString::operator!=(const MyString & b)
171 {
172     return !(*this == b);
173 }
174 bool MyString::operator==(const MyString & b)
175 {
176     if(b.size_ == size_){
177         return memcmp(b.data_, data_, size_) == 0;
178     }
179     return false;
180 }
181 //It's not good to do this because it will do 2 alloc.s & dealloc.s(temp. var in c++)
182 MyString MyString::operator+(const MyString &b)//will concat the two string.
183 {
184     MyString tmp;
185     memcpy(tmp.ReSizeBuf(size_ + b.size_), data_, size_);
186     memcpy(tmp.data_ + size_, b.data_, BufSize(b.size_));
187     return tmp;
188 }
189 MyString& MyString::operator+=(const MyString &b)
190 {
191     char* tmp = BufSize(size_) < BufSize(size_ + b.size_) ?
192         new char [BufSize(size_ + b.size_)] : data_ ;
193     if(tmp != data_){
194         memcpy(tmp, data_, size_);
195     }
196     memcpy(tmp + size_, b.data_, BufSize(b.size_));
197     if(tmp != data_){
198         delete [] data_;
199     }
200     data_ = tmp;
201     size_ = size_ + b.size_;
202     return *this;
203 }
204
205 char& MyString::operator[](const int idx)
206 {
207     assert(idx < size_);
208     return *(data_ + idx);
209 }
210 std::ostream & operator<<(std::ostream &out, const MyString& s)
211 {
212     return s.data_ ? out << s.data_ : out << "";
213 }
214
215
216 void test1(){
217     std::string ss = "12345";
218     std::string::iterator itr;
219     for(itr = ss.begin(); itr != ss.end(); itr++)
220     {
221         printf("%c ", *itr);
222     }
223     printf("\n");
224 }
225 void test_MyString()
226 {
227     MyString s1;
228     MyString s2("Hello");
229     MyString s3 = "1234";
230     MyString s4(s3);
231     std::cout << s1 << s2 << s3 << s4 << std::endl;
232 }
233
234 void test_operator()
235 {
236     MyString s1 = "Hello";
237     MyString s2 = "Hi";
238     MyString s3 = ", there";
239     MyString s4, s5("fff");
240     std::cout << s4 << " " << s4.size() << ";" << s5 << " " << s5.size() <<std::endl;
241     s4 = s5 = s1;
242     std::cout << s4 << " " << s4.size() << ";" << s5 << " " << s5.size()<<std::endl;
243
244     std::cout << (s2 == s1 ? "yes" : "no") << std::endl;
245     s2 = s1;
246     std::cout << (s2 != s1 ? "no" : "yes") << std::endl;
247
248     std::cout << s2 + s3 << std::endl;
249     s2 += s3;
250     std::cout << s2 << " " << s2.size() << std::endl;
251
252     s2[0] = 'K';
253     std::cout << s2 << std::endl;
254 }
255
256 void test_iterator()
257 {
258     MyString ssx = "Hi, My name is...";
259 /*
260 the post-increment ==> itr++ ==> will call MyString::Iterator::operator++(0)
261 pre-increment.cc:195:6: error: no ‘operator++(int)’ declared for postfix ‘++’ [-fpermissive]
262    itr++){
263       ^
264     for(MyString::Iterator itr = ssx.begin();
265         itr != ssx.end();
266         itr++){
267             std::cout << *itr << " " << std::endl;
268         }
269 */
270     for(MyString::Iterator itr = ssx.begin();
271         itr != ssx.end();
272         ++itr){
273             std::cout << *itr << " ";
274         }
275         std::cout << std::endl;
276 }
277 void test_MyTest()
278 {
279     MyString testss = "Hi, My Name is REM.";
280     std::cout << MyTest::distance<MyString::Iterator>(testss.begin(), testss.end()) <<std::endl;
281     //std::distance() is actually included at the <iterator>
282 }
283
284
285 void testobegin()
286 {
287     MyString testss = "Hi, I am Rem.Nice to meet you.";
288     MyString::OuputIterator oitr = testss.obegin();
289     oitr.print();
290 }
291 int main()
292 {
293     test_MyTest();
294     return 0;
295 }

用上一篇实现的那个MyString类做测试.(上一篇实现了适用于STL, 这里直接改成适用于MyTest)

  • 编译 & 运行:
g++ MyString_MyTest_Portable.cc -o test
./test
19

TODO

  • 3个遗留问题
  • 实现一个模拟STL的tag-dispatching.

这部分终于尝试了一下做了一点.STL里除了原理部分还有一大堆的平台相关代码,看起来真是日了狗.

实现部分应该算是理解得比较ok了,接下来需要认真地研究一下各大container如何使用的.(无聊脸)

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 160,165评论 4 364
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,720评论 1 298
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 109,849评论 0 244
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,245评论 0 213
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,596评论 3 288
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,747评论 1 222
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,977评论 2 315
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,708评论 0 204
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,448评论 1 246
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,657评论 2 249
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,141评论 1 261
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,493评论 3 258
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,153评论 3 238
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,108评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,890评论 0 198
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,799评论 2 277
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,685评论 2 272

推荐阅读更多精彩内容

  • 【转载】原文地址:std::string详解作者:kieven2008 之所以抛弃char*的字符串而选用C++标...
    VAYY阅读 613评论 0 2
  • 这门课主要偏重于泛型编程(generic programming)以及底层对象模型(this,vptr,vtbl,...
    无心浪子阅读 415评论 0 1
  • 再读高效c++,颇有收获,现将高效c++中的经典分享如下,希望对你有所帮助。 1、尽量以const \enum\i...
    橙小汁阅读 1,156评论 0 1
  • 转至元数据结尾创建: 董潇伟,最新修改于: 十二月 23, 2016 转至元数据起始第一章:isa和Class一....
    40c0490e5268阅读 1,638评论 0 9
  • 早睡早起,这已经是前几个月我给自己定的目标了,可是,一天也没有坚持过,此时此刻,我有些懊恼,我是一个有拖延症的人...
    赵婉宁阅读 173评论 0 0