STL空间配置器

来源:互联网 发布:mac装windows系统好吗 编辑:程序博客网 时间:2024/05/22 12:57

1、什么是空间配置器?

  空间配置器负责空间配置与管理。配置器是一个实现了动态空间配置、空间管理、空间释放的class template。以内存池方式实现小块内存管理分配。关于内存池概念可以点击:内存池。

2、STL空间配置器产生的缘由

  在软件开发,程序设计中,我们不免因为程序需求,使用很多的小块内存(基本类型以及小内存的自定义类型)。在程序中动态申请,释放。这个过程过程并不是一定能够控制好的,于是乎出现以下问题:

问题1:就出现了内存碎片问题。(ps外碎片问题)

问题2:一直在因为小块内存而进行内存申请,调用malloc,系统调用产生性能问题。

注:内碎片:因为内存对齐/访问效率(CPU取址次数)而产生 如 用户需要3字节,实际得到4或者8字节的问题,其中的碎片是浪费掉的。

  外碎片:系统中内存总量足够,但是不连续,所以无法分配给用户使用而产生的浪费。如图:

(1)一级空间配置器和二级空间配置器实现思路:

空间配置策略:

 用户申请空间大于128?

  yes:调用一级空间配置器

  no:调用二级空间配置器

 客户端可以通过宏__USE_MALLOC进行自定义选择是否使用二级空间配置器。

  STL设计了双层级配置器,第一级配置直接使用malloc()和free();第二级配置器则视情况采用不同的策略,当配置区大于128bytes时,直接调用第一级配置器;当配置区块小于128bytes时,遍不借助第一级配置器,而使用一个memory pool来实现。究竟是使用第一级配置器还是第二级配置器,由一个__USE_MALLOC宏定义来控制。SGI STL中默认使用第二级配置器。

3、一级空间配置器

主要是Allocate()函数和Dellocate()函数,直接封装了malloc,free进行处理,增加了C++中的set_handler机制,增加内存分配时客户端可选处理机制。

 1 #define __TRACE_DEBUG(...)  \ 2     __trace_debug(__FUNCTION__ , __FILE__, __LINE__, __VA_ARGS__); 3  4 typedef void (*HANDLE_FUNC)(); 5  6 template<int inst> 7 class MallocAllocTemplate 8 { 9 public:10     //static void(*__malloc_alloc_oom_handler)();11     static HANDLE_FUNC _malloc_alloc_oom_handler;12     13     void* OOM_Malloc(size_t n)14     {15         while (1)//死循环一直申请空间,直到申请成功,或失败抛异常16         {17             if (_malloc_alloc_oom_handler == 0)18             {19                 throw bad_alloc();20             }21             _malloc_alloc_oom_handler();//释放内存22             void* second = malloc(n); //再次申请空间23             if (second)24                 return second;25         }26     }27     // 1: 分配内存成功, 则直接返回28     // 2: 若分配失败, 则检查是否设置处理的handler,29     //有则调用以后再分配。 不断重复这个过程, 直到分配成功为止。30     //没有设置处理的handler, 则直接结束程序。31     static void* Allocate(size_t n)32     {33         __TRACE_DEBUG("一级空间配置器申请%ubytes\n", n);34 35         void* first = malloc(n);36         if (first == NULL)  //第一次申请空间失败37         {38             first = OOM_Malloc(0);39         }40         return first;41     }42     //realloc实现机制与allocate类似43     void* OOM_Realloc(size_t n)44     {45         while (1)//死循环一直申请空间,直到申请成功,或失败抛异常46         {47             if (_malloc_alloc_oom_handler == 0)48             {49                 throw bad_alloc();50             }51             _malloc_alloc_oom_handler();52             void* second = realloc(n); 53             if (second)54                 return second;55         }56     }57     static void* Rllocate(size_t n)58     {59         __TRACE_DEBUG("一级空间配置器申请%ubytes\n", n);60 61         void* first = realloc(n);62         if (first == NULL) 63         {64             first = OOM_Realloc(0);65         }66         return first;67     }68 69     static void Delloctate(void* p, size_t n)70     {71         __TRACE_DEBUG("一级空间配置器释放%ubytes\n", n);72         free(p);73     }74 75     static HANDLE_FUNC SetMallocHandler(HANDLE_FUNC f)76     {77         HANDLE_FUNC old = f;78         __malloc_alloc_oom_handler = f;79         return old;80     }81 //     static void(*SetMallocHandler(void(*f)()))()82 //     {83 //         void(*old)() = __malloc_alloc_oom_handler;84 //         __malloc_alloc_oom_handler = f;85 //         return(old);86 //     }87 private:88 };89 90 //分配内存失败处理函数的句柄函数指针91 template<int inst>92 HANDLE_FUNC __MallocAllocTemplate<inst>::__malloc_alloc_oom_handler = NULL;

SetMallocHandler(HANDLE_FUNC f)  

  malloc,free,realloc等库函数是向系统申请内存并且操作的函数。平时我们并不太会遇到内存空间分配不出来的情况,但是如果这一套程序是运行在服务器上的,各种各样的进程都需要内存。这样频繁的分配内存,终有一个时候,服务器再也分配不出内存,那么空间配置器该怎么办呢?这个函数指针指向的句柄函数就是处理这种情况的设计。

       SetMallocAllocHander()一般是自己设计的一种策略。这种策略想要帮助操作系统得到内存空间用以分配。所以,设计这个函数就是一个提升空间配置器效率的一个方法。如果并不想设计这个策略,也可以把句柄函数初始化为0。

__TRACE_DEBUG使用

  对于内存池的内部实现过程共还是比较复杂的,虽然代码量,函数比较简单。但是调用过程可能比较复杂。这时,如果我们选择debug调试,过程会相当的繁琐,需要仔细记录调用堆栈过程以及数据流向,逻辑变更等。对于楼主这种水货来说,估计完事就要苦了。

  所以,就__TRACE_DEBUG使用进行跟踪,打印数据流向,逻辑走向,文件,函数,方法,行位置。那么我们就能根据这个记录进行程序的排错以及调优了。

 1 //通过__TRACE_DEBUG做白盒测试 2  3 //#define __DEBUG__ 4 static string GetFileName(const string& path) 5 { 6     char ch = '/'; 7  8 #ifdef _WIN32 9     ch = '\\';10 #endif11 12     size_t pos = path.rfind(ch);13     if (pos == string::npos)14         return path;15     else16         return path.substr(pos + 1);17 }18 19 // 用于调试追溯的trace log20 inline static void __trace_debug(const char* function,21     const char * filename, int line, char* format, ...)22 {23     // 读取配置文件24 #ifdef __DEBUG__25     // 输出调用函数的信息26     fprintf(stdout, "【 %s:%d】%s", GetFileName(filename).c_str(), line, function);27 28     // 输出用户打的trace信息29     va_list args;30     va_start(args, format);31     vfprintf(stdout, format, args);32     va_end(args);33 #endif34 }35 36 #define __TRACE_DEBUG(...)  __trace_debug(__FUNCTION__ , __FILE__, __LINE__, __VA_ARGS__);

4、二级空间配置器

二级空间配置器的实现就比较复杂了,主要由内存池以及伙伴系统:自由链表组成。

  1 template<bool threads, int inst>  2 class DefaultAllocTemplate  3 {  4     enum { __ALIGN = 8 };  //(排列基准值,即排列间隔)  5     enum { __MAX_BYTES = 128 };  //最大值  6     enum { __NFREELISTS = __MAX_BYTES / __ALIGN };  //排列链  7 public:  8     static size_t FreeList_index(size_t n) //计算应该去取内存块的相应位置,对齐  9     { 10         return (n + __ALIGN - 1) / __ALIGN - 1; 11     } 12     static size_t Round_up(size_t bytes) //对齐 13     { 14         return ((bytes + __ALIGN - 1) & ~(__ALIGN - 1)); 15     } 16  17     // 到内存池申请nobjs个对象 18     static char* ChunkAlloc(size_t bytes, size_t& nobjs) 19     { 20         size_t needBytes = bytes * nobjs; 21         size_t leftBytes = _endfree - _startfree; 22         if (needBytes <= leftBytes) 23         { 24             __TRACE_DEBUG("狭义内存池有足够%u个对象\n", nobjs); 25  26             char* ret = _startfree; 27             _startfree += needBytes; 28             return ret;  //申请到20个 29         } 30         else if(leftBytes > bytes){ 31             nobjs = leftBytes / needBytes; 32             __TRACE_DEBUG("狭义内存池只有%u个对象\n", nobjs); 33  34             char* ret = _startfree; 35             _startfree += nobjs; 36             return ret;  //申请到1~19个 37         } 38         else 39         { 40             //处理余下的小块内存 41             if (leftBytes > 0)//挂到自由链表上的对应位置(头插) 42             { 43                 size_t index = FreeList_index(leftBytes); 44                 ((obj*)_startfree)->_freelistlink = _freeList[index]; 45                 _freeList[index] = (obj*)_startfree; 46             } 47  48             //一个也没有 49             size_t sizeToGet = needBytes * 2 + Round_up(_heapsize >> 4); 50             __TRACE_DEBUG("一个对象都没有,到系统申请%ubytes\n", sizeToGet); 51  52             _startfree = (char*)malloc(sizeToGet); 53             if (_startfree == NULL) 54             { 55                 // 系统已经没有足够的内存-尽力为之 56                 // 到更大的自由链表中去取内存块,置入内存池 57                 for (size_t i = FreeList_index(bytes); i < __NFREELISTS; i++) 58                 { 59                     if (_freeList[i]) //(头删) 60                     { 61                         _startfree = (char*)_freeList[i]; 62                         _freeList[i] = ((obj*)_startfree)->_freelistlink; 63                         _endfree = _startfree + (i + 1) * __ALIGN; 64                         return ChunkAlloc(bytes, nobjs);//重新申请 65                     } 66                 } 67                 // 山穷水尽 -- 求助一级空间配置器 68                 //自由链表中也没有分配到内存, 则再到一级配置器中分配内存, 69                 //一级配置器中可能有设置的处理内存, 或许能分配到内存。 70                 _endfree = NULL; 71                 _startfree = (char*)MallocAllocTemplate<0>::Allocate(sizeToGet); 72             } 73             //更新内存池 74             _heapsize += sizeToGet; 75             _endfree = _startfree + sizeToGet; 76             return ChunkAlloc(bytes, nobjs);  //重新申请 77         }  78      } 79     // 填充自由链表 80     static void* Refill(size_t bytes) 81     { 82         size_t nobjs = 20;  83         __TRACE_DEBUG("到狭义内存池取%u个%ubytes字节的对象\n", nobjs, bytes); 84  85         char* chunk = ChunkAlloc(bytes, nobjs); 86         __TRACE_DEBUG("取到了%u个对象,返回一个对象,将剩余的%u对象挂到自由链表的下面\n", nobjs, nobjs - 1); 87  88         if (nobjs == 1)//如果只分配到一块,直接使用它 89         { 90             return chunk; 91         } 92         size_t index = FreeList_index(bytes); 93         obj *cur = NULL; 94         obj *next = NULL; 95         for (size_t i = 1; i < nobjs; i++) 96         { 97             next = (obj*)(chunk + i*bytes); 98             if (_freeList[index] == NULL) 99             {100                 _freeList[index] = next;101             }102             else {103                 cur->_freelistlink = next;104             }105             cur = next;106         }107         if (cur)108         {109             cur->_freelistlink = NULL;110         }111         return chunk;112     }113     static void* Allocate(size_t n)114     {115         __TRACE_DEBUG("二级空间配置器申请%ubytes\n", n);116 117         if (n>__MAX_BYTES)118         {119             return MallocAllocTemplate<0>::Allocate(n);120         }121 122         // ps:多线程环境需要考虑加锁123         size_t index = FreeList_index(n);  //计算n在自由链表中的位置124         if (_freeList[index] == NULL)  //自由链表为空125         {126             __TRACE_DEBUG("在freeList[%u]下面没有内存块对象\n", index);127 128             return Refill(Round_up(0));  //获取大块内存插入到自由链表中129         }130         else{  //自由链表不为空,取第一块,类似于头删131             __TRACE_DEBUG("在freeList[%u]取一个内存块对象\n", index);132 133             obj* result = _freeList[index];134             _freeList[index] = result->_freelistlink;135             return result;136         }137     }138     static void Dellocate(void* ptr, size_t n)139     {140 141         if (n > __MAX_BYTES) 142         {143             MallocAllocTemplate<0>::Dellocate(ptr, n);144             return;145         }146 147         // ps:多线程环境需要考虑加锁148         size_t index = FreeList_index(n);149         __TRACE_DEBUG("将释放的内存块对象挂到freeList[%u]\n", index);150         151         if (ptr)152         {153             obj* back = (obj*)ptr; //将释放的内存块对象挂到_freeList[index]上,类似于头插154             back->_freelistlink = _freeList[index];155             _freeList[index] = back;156         }158     }159 protected:160     //定义自由链表161     union obj162     {163         obj* _freelistlink;  //指向下一个内存块的指针164         char clientdata[1]; /* The client sees this.        */165     };166     static obj* _freeList[__NFREELISTS];//自由链表167 168     //狭义内存池169     static char* _startfree;  //内存池水位线开始处170     static char* _endfree;  //内存池水位线结束处171     static size_t _heapsize;   //从系统堆分配的总大小172 };

代码中逻辑明了,而且已经注释得很清楚了,这里就不在赘述了。

 1 template <bool threads, int inst> 2 char* DefaultAllocTemplate<threads, inst>::_startfree = 0; 3  4 template <bool threads, int inst> 5 char* DefaultAllocTemplate<threads, inst>::_endfree = 0; 6  7 template <bool threads, int inst> 8 size_t DefaultAllocTemplate<threads, inst>::_heapsize = 0; 9 10 template <bool threads, int inst>11 typename DefaultAllocTemplate<threads, inst>::obj* DefaultAllocTemplate<threads, inst>::_freeList[__NFREELISTS] = { 0 };12 13 #ifdef __USE_MALLOC14 typedef MallocAllocTemplate<0> alloc;15 #else16 typedef DefaultAllocTemplate<false, 0> alloc;17 #endif //__USE_MALLOC18 19 template<class T, class Alloc>20 class SimpleAlloc21 {22 public:23     static T* Allocate(size_t n)24     {25         return 0 == n ? 0 : (T*)Alloc::Allocate(n * sizeof(T));26     }27 28     static T* Allocate(void)29     {30         return (T*)Alloc::Allocate(sizeof(T));31     }32 33     static void Dellocate(T *p, size_t n)34     {35         if (0 != n)36             Alloc::Dellocate(p, n * sizeof(T));37     }38 39     static void Dellocate(T *p)40     {41         Alloc::Dellocate(p, sizeof(T));42     }43 };44 45 void TestAlloc1()46 {47     void *p1 = DefaultAllocTemplate<false, 0>::Allocate(200);48      DefaultAllocTemplate<false, 0>::Dellocate(p1, 200);49 50      void *p2 = DefaultAllocTemplate<false, 0>::Allocate(25);51      void *p3 = DefaultAllocTemplate<false, 0>::Allocate(25);52  53      DefaultAllocTemplate<false, 0>::Dellocate(p2, 25);54      DefaultAllocTemplate<false, 0>::Dellocate(p3, 25);55 }56 57 void TestAlloc2()58 {59     cout << " 测试系统堆内存耗尽 " << endl;60 61     DefaultAllocTemplate<false, 0>::Allocate(1024 * 1024 * 1024);62     //DefaultAllocTemplate<false, 0>::Allocate(1024 * 1024 * 512);63     //DefaultAllocTemplate<false, 0>::Allocate(1024 * 1024);64 65     // 不好测试,说明系统管理小块内存的能力还是很强的。66     for (int i = 0; i < 1000; ++i)67     {68         DefaultAllocTemplate<false, 0>::Allocate(128);69     }70 }

 TestAlloc1()

TestAlloc2()

5、空间配置器存在的问题

(1)貌似二级空间配置器中的空间重头到尾都没看到他归还给系统。那么问题就是,内存池空间何时释放?

对于这个问题,在回头浏览一下源码及结构图,你就会发现,大于128的内存,客户程序Deallocate之后会调free释放掉,归还给了系统。

  但是,内存池中获取的空间,最终,假定用户都调用Dealloc释放调了,那么他们又在哪里呢?

    没有还给系统,没有在内存池,在自由链表中。

Got it:程序中不曾释放,只是在自由链表中,且配置器的所有方法,成员都是静态的,那么他们就是存放在静态区。释放时机就是程序结束。

 (2)如果需要释放,那么应该怎么处理呢?

  因为真正可以在程序运行中就归还系统的只有自由链表中的未使用值,但是他们并不一定是连续的(用户申请空间,释放空间顺序的不可控制性),所以想要在合适时间(eg一级配置器的handler中释放,或者设置各阀值,分配空间量到达时处理),就必须保证释放的空间要是连续的。保证连续的方案就是:跟踪分配释放过程,记录节点信心。释放时,仅释放连续的大块。

 (3)STL空间配置器的效率

  既然已经存在,而又被广泛使用,那么,整体的效率,以及和STL内部容器之间的使用配合还是没问题的。什么时候使用空间配置器最高效呢?就是你使用进行类似于容器中频繁的插入删除操作,而频繁的操作小块内存时,配置器就是比较高效的了,它的时间复杂度这个时候可以达到O(1)。而且避免了外碎片问题。

但是,我们考虑几种情况:

  a. 用户只需要无限的char类型空间,然而配置器中却对齐到8,于是乎,整个程序中就会有7/8的空间浪费。

  b.对于假定用户申请N次8空间,将系统资源耗到一定程度,然后全部释放了,自由链表中的空间都是连续的。却没有释放。

    但是:用户需要申请大于8的空间时,却依旧没有空间可用。

总之:这个问题就是,空间可能全部积攒在小块自由链表中,却没有用户可用的。这就尴尬了。

 附:利用空间配置器给容器分配空间

  1 //myList.h  2 #pragma once  3    4  #include "myAllocator.h"  5    6  template<class T>  7  struct  ListNode  8  {  9      ListNode<T>* _prev; 10      ListNode<T>* _next; 11   12      T _data; 13  }; 14   15  template<class T, class Ref, class Ptr> 16  struct ListIterator 17  { 18      typedef ListNode<T> Node; 19      typedef ListIterator<T, Ref, Ptr> Self; 20     Node* _node; 21  22      ListIterator(Node* node) 23          :_node(node) 24      {} 25      Ref operator*() 26      { 27          return _node->_data; 28      } 29      Ptr operator->() 30      { 31          return &(operator*()); 32      } 33      Self& operator++() 34      { 35          _node = _node->_next; 36          return *this; 37      } 38      Self operator++(int) 39      { 40          Self tmp(*this); 41          _node = _node->_next; 42          return tmp; 43      } 44      Self& operator--() 45      { 46          _node = _node->_prev; 47          return *this; 48      } 49      Self operator--(int) 50      { 51          Self tmp(*this); 52          _node = _node->_prev; 53          return tmp; 54      } 55      bool operator==(const Self& s) const 56      { 57          return _node = s._node; 58      } 59      bool operator!=(const Self& s) const 60      { 61          return _node != s._node; 62      } 63  }; 64   65  template<class T, class Alloc = alloc> 66  class List 67  { 68      typedef ListNode<T> Node; 69      typedef SimpleAlloc<Node, Alloc> ListAllocator; 70  public: 71      typedef ListIterator<T, T&, T*> Iterator; 72      typedef ListIterator<T, const T&, const T*> ConstIterator; 73   74      Iterator Begin() 75      { 76          return _head->_next; 77      } 78      ConstIterator Begin() const 79      { 80          return _head->_next; 81      } 82      Iterator End() 83      { 84          return _head; 85      } 86      ConstIterator End() const 87      { 88          return _head; 89      } 90   91      List() 92      { 93          _head = Create(T()); 94          _head->_next = _head; 95          _head->_prev = _head; 96      } 97      Node* Create(const T&x) 98      { 99          Node* _node = ListAllocator::Allocate();//申请空间100          new(&_node->_data)T(x); //构造对象101  102          _node->_data = x;103          _node->_next = NULL;104          _node->_prev = NULL;105  106          return _node;107      }108      ~List()109      {110          Clear();111          DestoryNode(_head);112          _head = NULL;113      }114      void Clear()115      {116          Node* cur = _head->_next;117          while (cur!=_head)118          {119              Node* next = cur->_next;120              DestoryNode(cur);121              cur = next;122          }123          _head->_next = _head;124          _head->_prev = _head;125      }126      void DestoryNode(Node* node)127      {128          (&node->_data)->~T();129          ListAllocator::Dellocate(node);130      }131  132      void PushBack(const T& x)133      {134          Node* _tail = _head->_prev;135          Node* tmp = Create(x);136  137          _tail->_next = tmp;138          tmp->_prev = _tail;139          _head->_prev = tmp;140          tmp->_next = _head;141  142          Insert(--End(), x);143      }144      void PushFront(const T& x)145      {146          Insert(Begin(), x);147      }148      void Insert(Iterator it, const T& x)149      {150          Node* pos = it._node;151          Node* tmp = Create(x);152          Node* prev = pos->_prev;153          assert(pos);154  155          prev->_next = tmp;156          tmp->_prev = prev;157          tmp->_next = pos;158          pos->_prev = tmp;159      }160      void PopBack()161      {162          Erase(--End());163      }164      void PopFront()165      {166          Erase(Begin());167      }168      Iterator Erase(Iterator& it)169      {170          Node* pos = it._node;171          Node* prev = pos->_prev;172          Node* next = pos->_next;173          assert(pos&&pos != _head);174  175          prev->_next = next;176          next->_prev = prev;177  178          DestoryNode(pos);179          it._node = prev;180          return next;181      }182  private:183      Node* _head;184  };185  186  void Print(const List<int>& l1)187  {188      List<int>::ConstIterator it = l1.Begin();189      while (it != l1.End())190      {191          cout << *it << " ";192          it++;193      }194      cout << endl;195  }196  197  void TestList1()198  {199      List<int> l1;200      l1.PushBack(10);201      l1.PushBack(12);202      l1.PushBack(13);203      l1.PushBack(19);204      l1.PushBack(14);205      l1.PushBack(17);206      Print(l1);207      List<int>::Iterator it2 = l1.Begin();208      while (it2!=l1.End())209      {210          if (*it2%2==0)211          {212              l1.Erase(it2);213          }214          *it2++;215      }216      Print(l1);217  }218  219  void TestList2()220  {221      List<string> l2;222      l2.PushBack("ping");223      l2.PushBack("z");224      l2.PushBack("987");225      l2.PushFront("1111");226      l2.PushFront("2344");227      l2.PushFront("22222222222222");228      l2.PushFront("34567");229      List<string>::Iterator it = l2.Begin();230      while (it != l2.End())231      {232          cout << *it << " ";233          ++it;234      }235      cout << endl;236  }
  1 //myVector.h   2 #pragma once  3  #include "myAllocator.h"  4    5  template<class T, class Alloc = alloc>  6  class Vector  7  {  8      typedef SimpleAlloc<T, Alloc> DataAllocator;  9  public: 10      typedef T* Iterator; 11      typedef const T* ConstIterator; 12   13      Vector() 14          :_start(NULL) 15          ,_finish(NULL) 16          ,_endofStorage(NULL) 17      {} 18      ~Vector() 19      { 20          for (size_t i=0;i<Size();i++) 21          { 22              (_start + i)->~T(); 23          } 24          DataAllocator::Dellocate(_start, Capacity()); 25      } 26      void PushBack(const T& x) 27      { 28          if (_finish == _endofStorage) 29          { 30              size_t size = Size(); 31              size_t newsize = size ? size * 2 : 3; 32              Expand(newsize); 33          } 34   35          new(_finish)T(x); 36          ++_finish; 37      } 38      void Expand(size_t n) 39      { 40          if (n > Capacity()) 41          { 42              size_t size = Size(); 43              size_t capacity = Capacity(); 44              T* tmp = DataAllocator::Allocate(n); 45              for (size_t i= 0;i<size;++i) 46              { 47                  new(tmp + i)T(_start[i]); 48                  (_start + i)->~T(); 49              } 50   51              DataAllocator::Dellocate(_start, capacity); 52              _start = tmp; 53              _finish = _start + size; 54              _endofStorage = _start + capacity; 55          } 56      } 57      void Reserver(size_t n) 58      { 59          Expand(n); 60      } 61      inline size_t Size() 62      { 63          return _finish - _start; 64      } 65      inline size_t Capacity() 66      { 67          return _endofStorage - _start; 68      } 69      Iterator Begin() 70      { 71          return _start; 72      } 73      Iterator End() 74      { 75          return _finish; 76      } 77      T& operator[](size_t pos) 78      { 79          assert(pos < Size()); 80          return _start[pos]; 81      } 82      const T& operator[](size_t pos) const 83      { 84          assert(pos < Size()); 85          return _start[pos]; 86      } 87  protected: 88      Iterator _start;  //指向顺序表头 89      Iterator _finish;  //指向指向顺序表最后一个元素的下一个位置 90      Iterator _endofStorage;  //指向顺序表尾部 91  }; 92   93  void TestVector1() 94  { 95      Vector<int> v; 96      v.PushBack(0); 97      v.PushBack(1); 98      v.PushBack(2); 99      v.PushBack(3);100      v.PushBack(4);101  102      Vector<int>::Iterator it = v.Begin();103      while (it != v.End())104      {105          cout << *it << " ";106          ++it;107      }108      cout << endl;109  }110 111  void TestVector2()112  {113      Vector<string> v1;114      v1.PushBack("1111");115      v1.PushBack("2222");116      v1.PushBack("3333");117      v1.PushBack("3333");118 119      for (size_t i = 0; i < v1.Size(); ++i)120      {121          cout << v1[i] << " ";122      }123      cout << endl;124  }

 

The end.....