STL源码剖析(4):容器(vector)

  容器是很多人对STL的第一印象,vector,stack,queue,set,map等等都是容器。

  这里先介绍 STL中的序列式容器。

  所谓序列式容器,其中的元素可序(ordered),但未必有序(sorted)。C++ 本身提供了一个序列式容器——数组(array),STL中还提供了向量(vector),链表(list),堆栈(stack),队列(queue),优先队列(priority queue)等,其中stack和queue只是将deque(双端队列)设限而得到的,技术上可以被归为一种配接器(adaptor)。

Vector:

  vector的数据安排以及操作方式与array非常相似,两者的唯一差别在于空间的运用的灵活性。array是静态空间,一旦配置了就不能改变。vector是动态空间,随着元素的加入,它的内部机制会自行扩充空间以容纳新元素。因此,vector对内存的运用更加有效,更加有效。Vector的实现技术,关键是对大小的控制以及重新配置时的数据移动效率,主要分为三个步骤:配置新空间,数据移动,释放旧空间。

  Vector内部结构为:

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;
};

  vector使用的线性连续空间,其中start是目前使用空间的头,finish是目前使用空间的为尾,end_of_storage是可使用空间的尾。为了降低空间配置时的速度成本,vector实际配置的大小可能比目前vector存储的数据所需的空间要大,以备将来的扩充。如果满载并有新元素加入,就另觅新居。

  vector维护的是一个连续线性空间,所以无论其元素型别为何,普通指针都可以作为vector的迭代器。因为vector迭代器所执行的操作行为,如operator*,operator->,operator++,operator–,operator+,operator-,operator+=,operator-=,普通指针天生就具备。所以,vector提供的是Random Access Iterator。

  vector若空间不够,在动态增长时,并不是在原来空间之后接续新空间,而是以原大小的两倍配置一块较大空间,然后将原内容拷贝过来。因此,对vector的任何操作,一旦引起空间的重新配置,指向原vector的所有迭代器就都失效了。

 

  来看下vector几个常用操作的源码:

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;
  }
  void pop_back() {
    --finish;
    destroy(finish); //析构
}
template <class T, class Alloc>
void vector<T, Alloc>::insert_aux(iterator position, const T& x) {
  if (finish != end_of_storage) { //空间未满
    construct(finish, *(finish - 1)); //插入要增加一个元素,所以先在末尾新构造一个
    ++finish;
    T x_copy = x;
    copy_backward(position, finish - 2, finish - 1); //从position往后移动一个位置
    *position = x_copy; //插入
  }
  else {
    const size_type old_size = size(); //当前的空间大小
    const size_type len = old_size != 0 ? 2 * old_size : 1; //新的空间大小,为以前的两倍
    iterator new_start = data_allocator::allocate(len);//分配新的空间
    iterator new_finish = new_start;
    __STL_TRY {
      new_finish = uninitialized_copy(start, position, new_start); //复制position之前的数据
      construct(new_finish, x); //插入新元素
      ++new_finish;
      new_finish = uninitialized_copy(position, finish, new_finish); //复制position之后的数据
    }
    destroy(begin(), end());//析构原来的元素
    deallocate();//释放原来的空间
    start = new_start;
    finish = new_finish;
    end_of_storage = new_start + len;
  }
}

  其中copy_backward()代码如下:

template <class BidirectionalIterator1, class BidirectionalIterator2>
inline BidirectionalIterator2 __copy_backward(BidirectionalIterator1 first,
                                              BidirectionalIterator1 last,
                                              BidirectionalIterator2 result) {
  while (first != last) *--result = *--last;
  return result;
}

其他常见操作:

Iterators:

begin:Return iterator to beginning (public member function )

end:Return iterator to end (public member function )

Capacity:

size:Return size (public member function )

capacity:Return size of allocated storage capacity (public member function )

empty:Test whether vector is empty (public member function )

Element access:

operator[]:Access element (public member function )

at:Access element (public member function )

front:Access first element (public member function )

back:Access last element (public member function )

data:Access data (public member function )

Modifiers:

push_back:Add element at the end (public member function )

pop_back:Delete last element (public member function )

insert:Insert elements (public member function )

erase:Erase elements (public member function )

swap:Swap content (public member function )

clear:Clear content (public member function )

emplace :Construct and insert element (public member function )

emplace_back :Construct and insert element at the end (public member function )

时间: 2024-08-23 23:30:18

STL源码剖析(4):容器(vector)的相关文章

C++ 《STL源码剖析》学习-vector

本文章是笔者学习<STL源码剖析>的学习笔记,记录的是笔者的个人理解,因为个人的水平有限,难免会有理解不当的地方,而且该书出版的时间比较久,难免会有些不一样.如有不当,欢迎指出. vector是c++中经常用到的数据结构,而且在面试时也会有提及,因此了解vector很重要. 一说到vector,我们就很容易想到另外一个与它十分相似的数据结构,关于它们之间显著的差别,我觉得是在于空间运用的灵活性上.数组是静态的,在声明的时候就要指明其具体的空间大小,而vector是动态的,随着元素的增加,它内部

STL源码剖析---关联容器

标准关联容器分为set和map两大类,包括multiset和multimap,这些容器的底层机制都是RB-tree.标准之外的关联容器有hashtable 以及以此hash table为底层机制而完成的hash_set(散列集合) hash_map(散列映射表) hash_multiset  hash_multimap. 序列和关联容器各自的内部关系是内含的,例如heap内含一个vector,priority_quehe内含一个heap,stack和queue都内含一个deque,set/map

STL源码剖析—序列容器

对于STL中的容器,存在一定的内含关系,例如,heap内含一个vector,priority-queue内含一个hep,stack和queue都含有一个deque,set/map/multiset/multimap都内含一个RB-tree,hash_x都内含一个hashtable. 对于序列容器来说,vector和list的插入都是在指向迭代器之前进行插入.同时需要注意的就是,对于vector来说,调用erase()函数也就是清除了几个连续空间上的元素,调用其析构函数,并没用释放原来元素占用的内

STL源码剖析--各个容器迭代器的分类

五类迭代器如下: 1.输入迭代器:只读,一次传递 为输入迭代器预定义实现只有istream_iterator和istreambuf_iterator,用于从一个输入流istream中读取.一个输入迭代器仅能对它所选择的每个元素进行一次解析,它们只能向前移动.一个专门的构造函数定义了超越末尾的值.总是,输入迭代器可以对读操作的结果进行解析(对每个值仅解析一次),然后向前移动.    2.输出迭代器:只写,一次传递 这是对输入迭代器的补充,不过是写操作而不是读操作.为输出迭代器的预定义实现只有ost

STL源码剖析 容器 stl_vector.h

本文为senlie原创,转载请保留此地址:http://blog.csdn.net/zhengsenlie vector ---------------------------------------------------------------------- 描述: 1.迭代器 vector 维护的是一个连续线性空间,它的迭代器是普通指针, 能满足 RandomAccessIterator 所有必要条件:operator*, operator->,operator++,operator--,

STL源码剖析 容器 stl_hashtable.h

本文为senlie原创,转载请保留此地址:http://blog.csdn.net/zhengsenlie hashtable --------------------------------------------------------------------------- 二叉搜索树具有对数平均时间的表现,它建立在输入数据有足够的随机性的假设 hashtable 有常数平均时间的表现,基于统计,不需依赖输入元素的随机性 hashtalbe 的简单实现: 所有元素都 16-bits 不带正负

STL源码剖析 容器 stl_list.h

本文为senlie原创,转载请保留此地址:http://blog.csdn.net/zhengsenlie list ---------------------------------------------------------------------- ??为什么很多在算法库里有的算法还要在类的成员函数里重新实现一遍? -->1.因为算法库里的是通用的,对于具体的类来说效率不高. 比如说 reverse 如果直接用 stl_algo.h 里的 reverse,会再调用 iter_swap,

STL源码剖析 容器 stl_deque.h

本文为senlie原创,转载请保留此地址:http://blog.csdn.net/zhengsenlie deque ------------------------------------------------------------------------ ??一直看不懂 operator->() ,不明白它为什么不用接受参数,直接 return &(operator*()) 好像我们用迭代器的时候也不没怎么用到这个函数,甚至我都不会用 1.概述 vector 是单向开口的连续线性空

STL源码剖析 容器 stl_queue.h

本文为senlie原创,转载请保留此地址:http://blog.csdn.net/zhengsenlie queue ---------------------------------------------------------------------- stack 是一种配接器(adapter),以某种容器作为底部结构,改变其接口,使之符合"先进先出"的特性. SGI STL 默认以 deque 为 stack 底部结构 没有遍历行为,没有遍历器 示例: #include &l

stl源码剖析-序列式容器 之 list

较久以前学过数据结构,对链表的定义和行为结构有过了解,所以阅读源码学习stl定义的list容器的并不算吃力. list与vector都是两个常用的容器,与vector不同,list不是连续线性空间的,list是一个双向链表.每次插入或者删除一个元素,将配置或者释放一个元素空间,因此,list对于空间的运用有着绝对的精准,不会造成浪费现象.而且对于任何位置的元素插入或者删除,其操作时间永远是常数时间.(缺点是不能进行随机的访问) list节点 list链表本身和list节点是分开设计的,以下是li