STL源码剖析笔记

第二章 空间配置器(allocator)

空间配置器标准接口(allocator)

allocator配置器是SGI STL提供的标准接口,但它只是对::operator new()和::operator delete()做了一层简单封装,效率不高,所以SGI并没有使用它。

高效的空间配置器结构std::alloc

class Foo{...}; Foo* pf = new Foo; delete pf;

这里的new内部包括两步操作:

  • 调用::operator new()分配内存
  • 调用Foo的构造函数

delete也包括两步操作:

  • 调用Foo的析构函数
  • 调用::operator delete()释放内存

上面的operator new()里面其实调用了malloc(),operator delete()里面调用了delete()

为了提高这个过程的效率,STL allocator将这两个阶段的操作区分开来,分为内存操作和对象操作:

  1. 内存操作
    • 内存分配:alloc::allocator()
    • 内存释放:alloc::deallocator()
  2. 对象操作
    • 对象构造:::construct() -> placement new
    • 对象析构:::destroy()

空间配置器std::alloc定义在stl_alloc.h中,整个配置器分为两层,为了考虑小型区块可能造成的内部碎片问题,SGI设计了两层级配置器,第一级采用malloc()free(),第二级则视情况而定:若配置区块超过128bytes,则采用第一级配置器;否则就采用memory pool的方式即第二级配置器。而整个容器的allocator就是simple_alloc,其实是对std::alloc的简单封装,使之能符合STL标准。

typedef __malloc_alloc_template<0> malloc_alloc; // 第一级配置器 typedef __default_alloc_template<__NODE_ALLOCATOR_THREADS, 0> alloc; // 第二级配置器,默认 template<class T, class Alloc> class simple_alloc { public: static T *allocate(size_t n) { return 0 == n? 0 : (T*) Alloc::allocate(n * sizeof (T)); } static T *allocate(void) { return (T*) Alloc::allocate(sizeof (T)); } static void deallocate(T *p, size_t n) { if (0 != n) Alloc::deallocate(p, n * sizeof (T)); } static void deallocate(T *p) { Alloc::deallocate(p, sizeof (T)); } };

上面的模板参数Alloc就表示了配置器的种类,一级或二级。

一级配置器

template <int __inst> class __malloc_alloc_template { private: //用来处理内存不足的情况 static void* _S_oom_malloc(size_t); static void* _S_oom_realloc(void*, size_t); #ifndef __STL_STATIC_TEMPLATE_MEMBER_BUG static void (* __malloc_alloc_oom_handler)(); #endif public: static void* allocate(size_t __n) { void* __result = malloc(__n); if (0 == __result) __result = _S_oom_malloc(__n); return __result; } static void deallocate(void* __p, size_t /* __n */) { free(__p); } static void* reallocate(void* __p, size_t /* old_sz */, size_t __new_sz) { void* __result = realloc(__p, __new_sz); if (0 == __result) __result = _S_oom_realloc(__p, __new_sz); return __result; } }; // malloc_alloc out-of-memory handling #ifndef __STL_STATIC_TEMPLATE_MEMBER_BUG template <int __inst> void (* __malloc_alloc_template<__inst>::__malloc_alloc_oom_handler)() = 0; #endif

可以看出,一级配置器malloc_alloc仅仅是对malloc和free的简单封装,这里的__malloc_alloc_oom_handler函数指针默认为NULL,它类似于C++new里面的set_new_handler()函数,就是在空间分配失败时,会调用这个回调函数。

二级配置器

template <bool threads, int inst> class __default_alloc_template { private: //获取bytes最适合的块大小,比如7->8, 9->16, 22->24 static size_t _S_round_up(size_t __bytes) { return (((__bytes) + (size_t) _ALIGN-1) & ~((size_t) _ALIGN - 1)); } __PRIVATE: union _Obj { union _Obj* _M_free_list_link; char _M_client_data[1]; }; private: static _Obj* __STL_VOLATILE _S_free_list[]; static size_t _S_freelist_index(size_t __bytes) { return (((__bytes) + (size_t)_ALIGN-1)/(size_t)_ALIGN - 1); } //返回一个size为__n的对象,然后加入到对应的free list static void* _S_refill(size_t __n); //配置一大块内存 static char* _S_chunk_alloc(size_t __size, int& __nobjs); //内存池起始位置和结束位置 static char* _S_start_free; static char* _S_end_free; static size_t _S_heap_size; # ifdef __STL_THREADS //多线程环境会用到的锁 static _STL_mutex_lock _S_node_allocator_lock; # endif class _Lock; friend class _Lock; class _Lock { public: _Lock() { __NODE_ALLOCATOR_LOCK; } ~_Lock() { __NODE_ALLOCATOR_UNLOCK; } }; public: static void* allocate(size_t __n); static void deallocate(void* __p, size_t __n); static void* reallocate(void* __p, size_t __old_sz, size_t __new_sz); } ;

二级配置器的设计是为了对付小型区块内部碎片,为了管理区块,还需要一些额外内存存放管理信息cookie。它维护了一个链表数组,每个节点指向大小不同的内存块,大小为8、16、24...128byte。每次分配内存时,就选择合适大小的块分配。如果块不够,就从内存池中分配一大块内存,切成小块,加入到对应的链表中。如果内存池也不够用了,就调用malloc来补充内存池。

allocator

static void* allocate(size_t __n) { void* __ret = 0; //如果大于128byte,用第一级配置器 if (__n > (size_t) _MAX_BYTES) { __ret = malloc_alloc::allocate(__n); } else { _Obj* __STL_VOLATILE* __my_free_list = _S_free_list + _S_freelist_index(__n); //如果对应的free_list为空,那么就refill,否则就调整一下freelist头节点,然后返回之 _Obj* __RESTRICT __result = *__my_free_list; if (__result == 0) __ret = _S_refill(_S_round_up(__n)); else { *__my_free_list = __result -> _M_free_list_link; __ret = __result; } } return __ret; };

allocator函数先判断是否调用一级配置器。如果不是,就选择一个合适块大小的链表,然后分配一个块。如果链表为空,就refill填充内存。

refill

template <bool __threads, int __inst> void* __default_alloc_template<__threads, __inst>::_S_refill(size_t __n) { int __nobjs = 20; char* __chunk = _S_chunk_alloc(__n, __nobjs); _Obj* __STL_VOLATILE* __my_free_list; _Obj* __result; _Obj* __current_obj; _Obj* __next_obj; int __i; if (1 == __nobjs) return(__chunk); __my_free_list = _S_free_list + _S_freelist_index(__n); /* Build free list in chunk */ __result = (_Obj*)__chunk; *__my_free_list = __next_obj = (_Obj*)(__chunk + __n); for (__i = 1; ; __i++) { __current_obj = __next_obj; __next_obj = (_Obj*)((char*)__next_obj + __n); if (__nobjs - 1 == __i) { __current_obj -> _M_free_list_link = 0; break; } else { __current_obj -> _M_free_list_link = __next_obj; } } return(__result); }

refill先调用chunk_alloc去内存池分配20个块大小的内存。如果只返回一个块,就还是返回。否则就把分配来的内存切成小块,加入到对应块大小的链表中。

chunk_alloc

template <bool __threads, int __inst> char* __default_alloc_template<__threads, __inst>::_S_chunk_alloc(size_t __size, int& __nobjs) { char* __result; size_t __total_bytes = __size * __nobjs; size_t __bytes_left = _S_end_free - _S_start_free; // 内存池容量可以满足需求 if (__bytes_left >= __total_bytes) { __result = _S_start_free; _S_start_free += __total_bytes; return(__result); } // 可以满足一个block大小,就也返回 else if (__bytes_left >= __size) { __nobjs = (int)(__bytes_left/__size); __total_bytes = __size * __nobjs; __result = _S_start_free; _S_start_free += __total_bytes; return(__result); } else { //如果一个block都无法提供,那么就需要重新为内存池分配内存 //分配的大小为 两倍需求量 + 随分配次数而增加的附加量(heap_size初始为0) size_t __bytes_to_get = 2 * __total_bytes + _S_round_up(_S_heap_size >> 4); // Try to make use of the left-over piece. //如果内存池还有一丁点剩下的内存,就把他加入到对应的free list,尽量节约 if (__bytes_left > 0) { _Obj* __STL_VOLATILE* __my_free_list = _S_free_list + _S_freelist_index(__bytes_left); ((_Obj*)_S_start_free) -> _M_free_list_link = *__my_free_list; *__my_free_list = (_Obj*)_S_start_free; } _S_start_free = (char*)malloc(__bytes_to_get); //如果malloc分配失败 if (0 == _S_start_free) { size_t __i; _Obj* __STL_VOLATILE* __my_free_list; _Obj* __p; // Try to make do with what we have. That can't // hurt. We do not try smaller requests, since that tends // to result in disaster on multi-process machines. for (__i = __size; __i <= (size_t) _MAX_BYTES; __i += (size_t) _ALIGN) { __my_free_list = _S_free_list + _S_freelist_index(__i); __p = *__my_free_list; if (0 != __p) { *__my_free_list = __p -> _M_free_list_link; _S_start_free = (char*)__p; _S_end_free = _S_start_free + __i; return(_S_chunk_alloc(__size, __nobjs)); // Any leftover piece will eventually make it to the // right free list. } } _S_end_free = 0; // In case of exception. _S_start_free = (char*)malloc_alloc::allocate(__bytes_to_get); // This should either throw an // exception or remedy the situation. Thus we assume it // succeeded. } _S_heap_size += __bytes_to_get; _S_end_free = _S_start_free + __bytes_to_get; return(_S_chunk_alloc(__size, __nobjs)); } }

chunk_alloc先是判断自己的内存池里面的内存还够不够这次分配。如果够,就正常处理下,然后返回。如果只够一个block,也会返回。但是如果一个block也不够了呢?这时候就需要malloc了,具体要申请的是2 * __total_bytes + _S_round_up(_S_heap_size >> 4);,这个total_bytes好理解,就是我们需要的空间,而这个heap_size则是这个class template中的一个static data member,存储的是迄今为止用了多少heap memory,也就是malloc了多少内存,初始值为0。所以说,每次要申请的内存是两倍的需求量加上,一点点附加量。然后,malloc会比需求多点,给内存池存点余额,避免频繁的调用malloc,影响速度。而malloc失败后,会尝试把链表里面的mem block都收集到内存池中去,然后再去尝试满足这次分配。然后就是调用malloc_alloc

第三章 iterator and traits

iterator

迭代器是一种行为类似指针的对象,本文介绍iterator与traits的关系,以及对traits内容的补充。包含stl_iterator.h的部分内容,并用c++11对其进行略微改写。
上篇文章已经介绍了这五种类型的特征,它们只是为了激活重载机制而设定,并不需要其他成员。它们的定义如下:

//五种迭代器类型 struct input_iterator_tag{}; struct output_iterator_tag{}; struct forward_iterator_tag: public input_iterator_tag { }; struct bidirectional_iterator_tag: public forward_iterator_tag{ }; struct random_access_iterator_tag: public bidirectional_iterator_tag{ };

接下来是iterator的定义:

//自造的iterator最好继承下面的std::iterator template<typename Category, typename T, typename Distance = ptrdiff_t, typename Pointer = T*, typename Reference = T&> struct iterator { using iterator_category = Category; using value_type = T; using difference_type = Distance; using pointer = Pointer; using reference = Reference; }; //萃取机Traits //萃取出迭代器的特性 template<typename Iterator> struct iterator_traits { using iterator_category = typename Iterator::iterator_category; using value_type = typename Iterator::value_type; using difference_type = typename Iterator::difference_type; using pointer = typename Iterator::pointer; using reference = typename Iterator::reference; }; //针对原生指针的片特化版本 template<typename T> struct iterator_traits<T*> { using iterator_category = random_access_iterator_tag; using value_type = T; using difference_type = ptrdiff_t; using pointer = T*; using reference = T&; }; //pointer-to-const template<typename T> struct iterator_traits<const T*> { using iterator_category = random_access_iterator_tag; using value_type = T; using difference_type = ptrdiff_t; using pointer = const T*; using reference = const T&; }; //确定某个迭代器类型,并返回该类型的一个迭代器对象,方便进行函数重载 template<typename Iterator> inline typename iterator_traits<Iterator>::iterator_category iterator_category(const Iterator&) { using category = typename iterator_traits<Iterator>::iterator_category; return category(); } //确定某个迭代器的distance type template<typename Iterator> inline typename iterator_traits<Iterator>::difference_type* distance_type(const Iterator&) { return static_cast<typename iterator_traits<Iterator>::difference_type*>(nullptr); } //确定某个迭代器的value type template<typename Iterator> inline typename iterator_traits<Iterator>::value_type* value_type(const Iterator&) { return static_cast<typename iterator_traits<Iterator>::value_type*>(nullptr); } //整个distance函数的实现,第三个参数只是激活重载机制,无其他用处 template<typename InputIterator> inline typename iterator_traits<InputIterator>::difference_type _distance(InputIterator first, InputIterator last, input_iterator_tag) { typename iterator_traits<InputIterator>::difference_type n = 0; while(first != last) { ++first; ++n; } } template<typename RandomAccessIterator> inline typename iterator_traits<RandomAccessIterator>::difference_type _distance(RandomAccessIterator first, RandomAccessIterator last, random_access_iterator_tag) { return last - first; } //对外接口,适应不同类型的迭代器 template<typename InputIterator> inline typename iterator_traits<InputIterator>::difference_type distance(InputIterator first, InputIterator last) { //先利用traits确定迭代器的类型 using category = typename iterator_traits<InputIterator>::iterator_category; //利用函数重载,第三个参数只是激活重载机制,无其他用处 _distance(first, last, category()); } //整个advance函数的实现,第三个参数只是激活重载机制,无其他用处 template<typename InputerIterator, typename Distance> inline void _advance(InputerIterator& i, Distance n, input_iterator_tag) { while(n--) ++i; } template<typename BidirectionalIterator, typename Distance> inline void _advance(BidirectionalIterator i, Distance n, bidirectional_iterator_tag) { if(n >= 0) while(n--) ++i; else while(n++) --i; } template<typename RandomAccessIterator, typename Distacne> inline void _advance(RandomAccessIterator& i, Distacne n, random_access_iterator_tag) { i += n; } //对外接口,适应不同类型的迭代器 template<typename InputIterator, typename Distance> inline void advance(InputIterator& i , Distance n) { _advance(i, n, iterator_category(i)); } #endif //STL_ITERATOR_H

主要讨论如何获取迭代器相应型别。使用迭代器时,很可能用到其型别,若需要声明某个迭代器所指对象的型别的变量,该如何解决。方法如下:

function template的参数推导机制

例如:

template<typename I, typename T> void func_impl(I iter, T t) { //T为迭代器所指对象的型别 T tmp; //..... } template<typename I> inline void func(I iter) { //func工作交给func_impl完成 func_impl(iter, *iter); } int main() { int i; func(&i); return 0; }

func_impl()是一个 function template,一旦被调用,编译器会自动进行template参数推导,从而导出型别T,无需自己指出型别,解决问题。迭代器相应型别不只是迭代器所指对象的型别一种而已,最常用的相应型别有五种,但并非任何情况都可利用上述template参数推导机制来取得。这就需要其他方法。

Traits编程技法

迭代器所指对象的型别,成为该迭代器的value type,上述模板参数推导并非全面可用,在需要value type作为函数返回值时,就不能解决了。template参数推导的只是参数而已。因此,声明内嵌型别就出现了。

例如:

template<typename T> struct MyIter { typedef T value_type; //内嵌型别声明 T* ptr; MyIter(T* p = nullptr):ptr(p) { } T& operator*() const { return *ptr;} //... }; template<typename I> typename I::value_type //函数func()的返回类型,为I类型迭代器中的value_type func(I ite) { return *ite; } int main() { MyIter<int> ite(new int(8)); cout<<func(ite); //输出8 return 0; }

func()函数的返回值必须加上关键字typename,用来告诉编译器这时一个模板类型。但并不是所有迭代器都为class type,原生指针就不是,它就不能定义内嵌型别。这时模板偏特化(template partial specialization)就能解决这个问题。

偏特化的意义

如果class template拥有一个以上的template参数,我们可以针对其中某个(或数个,但并非全部)template参数进行特化工作。也就是将泛化版本中的某些template参数给予明确的指定。

如:

template<typename U, typename V, typename T> class C { };

偏特化不是template参数U、V或T指定某个参数值,而是针对(任何)template参数更进一步的条件限制所设计出来的一个特化版本。看这个例子:

//泛化版本 template<typename T> class C { } //偏特化版本 template<typename T> class C<T*> { }; //解决原生指针的问题

如此便能解决前面的内嵌型别的问题。下面这个class template专门用来萃取迭代器的特性之一 :value_type

template<typename I> struct Iterator_traits //traits指的是特性 { typedef typename I::value_type value_type; };

如果I内定义了自己的value type,通过这个traits萃取出来的value type就是I::value_type,则前面的func(),可改为:

template<typename I> typename Iterator_traits<I>::value_type func(I ite) { return *ite; }

现在写出Iterator_traits的一个偏特化版本,这就解决了原生指针的问题,如果写成Iterator_traits<int*>::value_type,便得到int:

template<typename T> struct Iterator_traits<T*> { typedef T value_type; };

但针对“指向常数对象的指针”,Iterator_traits<const int*>::value_type可萃取到const int,此时若要得到non-const int,就要设计下面这一个偏特化版本:

template<typename T> struct Iterator_traits<const T*> { tpyedef T value_type; //当迭代器为pointer-to-const,得到non-const T };

现在迭代器MyIter、原生指针int*或const int *,都能通过Iterator_traits取出正确的value type。
所以,每个迭代器应以内嵌型别定义的方式定义出相应型别,以遍traits运作。

迭代器相应型别

value type

value type指迭代器所指对象的型别。

difference type

difference type表示两个迭代器之间的距离,也能表示一个容器的最大容量,对连续的空间而言,头尾的距离就为最大容量。如STL的count(),其返回值就必须使用迭代器的difference type:

template<typename I, typename T> typename iterator_traits<I>::difference_type //函数的返回值类型 count(I first, I last, const T& value) { typename iterator_traits<I>::difference_type n = 0; //记录个数 for( ; first != last; ++first) { if(*first == value) ++n; } return n; };

针对原生指针而写的偏特化版本,以c++内建的ptrdiff_t(定义于cstddef头文件)作为原生指针的difference type:

template<typename I> struct iterator_traits { //... typedef typename I::difference_type difference_type; }; //原生指针的偏特化版本 template<typename T> struct iterator_traits<T*> { typedef ptrdiff_t difference_type; }; //pointer-to-const的偏特化版本 template<typename T> struct iterator_traits<const T*> { typedef ptrdiff_t difference_type; };

现在,我们可以通过写

typename iterator_traits<I>::difference_type

来得到任何迭代器I的difference_type。

reference type

引用类型,传回一个迭代器所指对象的引用

pointer type

传回一个,指向迭代器所指之物的pointer。

Item& operator*() const { return *ptr; } Item* operator->() const { return ptr; }

Item&便是某个迭代器的reference type,而Item*便是其pointer type。在traits内:

template<typename I> struct iterator_traits { typedef typename I::pointer pointer; typedef typename I::reference reference; }; //针对原生指针的偏特化版本 template<typename T> struct iterator_traits<T*> { typedef T* pointer; typedef T& reference; }; //针对pointer-to-const的偏特化版本 template<typename T> struct iterator_traits<const T*> { typedef const T* pointer; typedef const T& reference; };

iterator_category

根据移动特性,迭代器被分为五类:

  • Input Iterator: read only
  • Output Iterator: write only
  • Forward Iterator: 允许写入型算法在这种迭代器所形成的区间上进行读写操作
  • Bidirectional Iterator: 可双向移动
  • Random Access Iterator: 涵盖所有指针算数能力,包括p+n,p-n,p[n]等等

它们从上到下,功能依次强化。
下面以advance()函数为例,该函数有两个参数迭代器p,数值n;函数内部将p累进n次。
下面针对三种不同迭代器进行示范:

//InputIterator template<typename InputIterator, typename Distance> void advance_II(InputIterator& i, Distance n) { //单向逐一前进 while(n--) ++i; } //BidirectionalIterator template<typename BidirectionalIterator, typename Distance> void advance_BI(BidirectionalIterator& i, Distance n) { //双向逐一前进 if(n >= 0) while(n--) ++i; else while(n++) --i; } //RandomAccessIterator template<typename RandomAccessIterator, typename Distance> void advance_RAI(RandomAccessIteraor& i, Distance n) { //双向跳跃前进 i += n; } //封装版本 template<typename InputIterator, typename Distance> void advance(InputIterator& i, Distance n) { //分别判断迭代器类型 if(is_random_access_iterator(i)) advance_RAI(i, n); else if(is_bidirectionl_iterator(i)) advance_BI(i, n); else advance_II(i, n); }

这样在执行期才决定使用哪个版本,会影响效率。最好能在编译器就选择正确版本,重载函数机制就解决了这个问题。
前面3个advance_xx()都有两个template 参数,类型不确定,为了形成重载函数,必须加上一个型别已经确定的函数参数。下面五个classes代表了五种迭代器类型:

//标记用的型别 struct input_iterator_tag { }; struct output_iterator_tag { }; struct forward_iterator_tag: public input_iterator_tag { }; struct bidirectional_iterator_tag: public forward_iterator_tag { }; struct random_access_iterator_tag: public bidirectional_iterator_tag { };

这些classes当做标记用,作为第三个参数,使能达到重载函数的目的:

//InputIterator template<typename InputIterator, typename Distance> void _advance(InputIterator& i, Distance n, input_iterator_tag) { //单向逐一前进 while(n--) ++i; } //ForwardIterator template<typename ForwardIterator, typenmae Distance> void _advance(ForwardIterator& i, Distance n, forward_iterator_tag) { _advance(i, n, input_iterator_tag()) } //BidirectionalIterator template<typename BidirectionalIterator, typename Distance> void _advance_BI(BidirectionalIterator& i, Distance n, bidirectional_iterator_tag) { //双向逐一前进 if(n >= 0) while(n--) ++i; else while(n++) --i; } //RandomAccessIterator template<typename RandomAccessIterator, typename Distance> void advance_RAI(RandomAccessIteraor& i, Distance n, random_access_iterator_tag) { //双向跳跃前进 i += n; }

每一个_advance()的最后一个参数只声明型别,是为了激活重载函数机制,并不需要参数名称,并且函数中并不使用该参数。下面是对外开放的接口,以调用不同的_advance()。

template<typename InputIterator, typename Distance> inline void advance(InputIterator& i, Distance n) { _advance(i, n, iterator_traits<InputIterator>::iterator_category()); } //iterator_category()在stl_iterator.h中定义 template<typename I> inline typename iterator_traites<I>::iterator_category iterator_category(const I&) { typedef typename iterator_traits<I>:iterator_category category; return category(); }

为满足上述行为,traits中增加相应型别:

template<typename I> struct iterator_traits { //... typedef typename I::iterator_category iterator_category; }; //针对原生指针的偏特化版本 template<typename T> struct iterator_traits<T*> { //... //原生指针为一种RandomAccessIterator typedef random_access_iterator_tag iterator_category; }; //针对pointer-to-const的偏特化版本 template<typename T> struct iterator_traits<const T*> { //... typedef randow_access_iterator_tag iterator_category; };

任何一个迭代器,它的类型应属于迭代器类型中功能最强大的类型如int* ,既是Random又是Bidirectional,既是Forward又是Input,他应为Random。

第四章 序列式容器

vector

概述

vector是动态数组,其底层空间会随着插入元素而自动扩充,并且是连续的内存。如果空间不够用,会扩充至两倍空间,会申请更大的一片空间,将原数据copy过去,并释放旧空间;如果两倍都不够用,比如空间大小为4,但要插入100个元素,那么就需要让空间增长n个大小因此如果空间被重新配置,那么原来的所有迭代器都会失效,这点尤为重要。

结构

template <class T, class Alloc = alloc> class vector { public: typedef T value_type; typedef value_type* pointer; typedef const value_type* const_pointer; typedef value_type* iterator; typedef const value_type* const_iterator; typedef value_type& reference; typedef const value_type& const_reference; typedef size_t size_type; typedef ptrdiff_t difference_type; // ... protected: typedef simple_alloc<value_type, Alloc> data_allocator; iterator start; iterator finish; iterator end_of_storage; void insert_aux(iterator position, const T& x); void deallocate() { if (start) data_allocator::deallocate(start, end_of_storage - start); } // ... ~vector() { destroy(start, finish); deallocate(); } };

vector使用的空间配置器为simple_allocstartfinish之间是已经使用的空间,finishend_of_storage直接是预留的未使用空间,每次当预留的空间都不足时,就会申请一块新的连续空间,将数据copy过去,并释放旧空间,这通过deallocate()完成。

常用函数

iterator begin() { return start; } iterator end() { return finish; } size_type size() const { return size_type(end() - begin()); } size_type capacity() const { return size_type(end_of_storage - begin()); } bool empty() const { return begin() == end(); } reference operator[](size_type n) { return *(begin() + n); } const_reference operator[](size_type n) const { return *(begin() + n); } vector() : start(0), finish(0), end_of_storage(0) {} vector(size_type n, const T& value) { fill_initialize(n, value); } vector(int n, const T& value) { fill_initialize(n, value); } vector(long n, const T& value) { fill_initialize(n, value); } explicit vector(size_type n) { fill_initialize(n, T()); }

需要注意的是size()capacity()的区别,前者是已存储数据的个数,后者是总的可用空间的大小。几个构造函数是通过fill_initialize()实现的,如果是POD类型的对象,则就和memset()相似,因为不需要调用构造函数,vector析构的时候也不需要调用每个对象的析构函数。如果对象有nontrivial constructor,才会调用构造和析构函数。

void reserve(size_type n) { if (capacity() < n) { const size_type old_size = size(); iterator tmp = allocate_and_copy(n, start, finish); destroy(start, finish); deallocate(); start = tmp; finish = tmp + old_size; end_of_storage = start + n; } } void resize(size_type new_size, const T& x) { if (new_size < size()) erase(begin() + new_size, end()); else insert(end(), new_size - size(), x); } void resize(size_type new_size) { resize(new_size, T()); } void swap(vector<T, Alloc>& x) { __STD::swap(start, x.start); __STD::swap(finish, x.finish); __STD::swap(end_of_storage, x.end_of_storage); }

reserve()是相对于capacity()来操作的,就是扩容总的存储空间,通过allocator new area -> copy old data to new area -> destroy old data -> deallocate old memory

resize()是相对于size()来操作的,就是调整已存在对象的个数。

swap()是通过交换内部的3个迭代器实现的,这很高效,并不是复制对象再释放内存。

insert

void push_back(const T& x) { if (finish != end_of_storage) { construct(finish, x); ++finish; } else insert_aux(end(), x); } iterator insert(iterator position, const T& x) { size_type n = position - begin(); if (finish != end_of_storage && position == end()) { construct(finish, x); ++finish; } else insert_aux(position, x); return begin() + n; } template <class T, class Alloc> void vector<T, Alloc>::insert(iterator position, size_type n, const T& x) { if (n != 0) { if (size_type(end_of_storage - finish) >= n) { T x_copy = x; const size_type elems_after = finish - position; iterator old_finish = finish; if (elems_after > n) { uninitialized_copy(finish - n, finish, finish); finish += n; copy_backward(position, old_finish - n, old_finish); fill(position, position + n, x_copy); } else { uninitialized_fill_n(finish, n - elems_after, x_copy); finish += n - elems_after; uninitialized_copy(position, old_finish, finish); finish += elems_after; fill(position, old_finish, x_copy); } } else { const size_type old_size = size(); const size_type len = old_size + max(old_size, n); iterator new_start = data_allocator::allocate(len); iterator new_finish = new_start; __STL_TRY { new_finish = uninitialized_copy(start, position, new_start); new_finish = uninitialized_fill_n(new_finish, n, x); new_finish = uninitialized_copy(position, finish, new_finish); } # ifdef __STL_USE_EXCEPTIONS catch(...) { destroy(new_start, new_finish); data_allocator::deallocate(new_start, len); throw; } # endif /* __STL_USE_EXCEPTIONS */ destroy(start, finish); deallocate(); start = new_start; finish = new_finish; end_of_storage = new_start + len; } } }

插入单个元素,都是先判断容量是否够,如果不够就会调insert_aux()

insert()也是先判断备用空间,如果充足,就将从position开始的元素后移,再将插入元素copy进来。如果备用空间不足,就先决定新的空间大小,这根据插入个数n来决定是旧长度的2倍还是旧长度+n:

const size_type old_size = size(); const size_type len = old_size + max(old_size, n);

erase

void pop_back() { --finish; destroy(finish); } iterator erase(iterator position) { if (position + 1 != end()) copy(position + 1, finish, position); --finish; destroy(finish); return position; } iterator erase(iterator first, iterator last) { iterator i = copy(last, finish, first); destroy(i, finish); finish = finish - (last - first); return first; }

pop_back()erase()就比较简单了,只是destroy()和调整那3个关键迭代器。


__EOF__

本文作者刘一凡
本文链接https://www.cnblogs.com/vlyf/p/11838458.html
关于博主:评论和私信会在第一时间回复。或者直接私信我。
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
声援博主:如果您觉得文章对您有帮助,可以点击文章右下角推荐一下。您的鼓励是博主的最大动力!
posted @   ManateeFan  阅读(302)  评论(0编辑  收藏  举报
编辑推荐:
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
· SQL Server 2025 AI相关能力初探
阅读排行:
· winform 绘制太阳,地球,月球 运作规律
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· AI与.NET技术实操系列(五):向量存储与相似性搜索在 .NET 中的实现
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 超详细:普通电脑也行Windows部署deepseek R1训练数据并当服务器共享给他人
点击右上角即可分享
微信分享提示