vector
- vector是动态空间,随着元素的加入,它的内部机制会自行扩充空间以容纳新元素。
vector的迭代器
vector维护的是一个连续线性空间,所以不论其元素型别为何,普通指针都可以作为vector的迭代器而满足所有必要条件,因为vector迭代器需要的操作行为,如operator*
、operator->
等,普通指针天生就具备。vector支持随机存取。
cpp
template <class T, class Alloc = alloc>
class vector
{
// ...
public:
typedef T value_type;
typedef value_type* iterator; // vector的迭代器是普通指针
// ...
};
若客端代码如下,则ivite
的型别其实就是int*
,svite
的型别其实就是Shape*
。
cpp
vector<int>::iterator ivite;
vector<Shape>::iterator svite;
vector的数据结构
为了降低空间配置时的速度成本,vector实际配置的大小可能比客户端需求量大一些,以备将来可能的扩充。这便是容量(capacity),一个vector的容量永远大于或者等于其大小。
cpp
template <class T, class Alloc = alloc>
class vector
{
// ...
protected:
iterator start; // 目前使用空间的头
iterator finish; // 目前使用空间的尾
iterator end_of_storage; // 目前可用空间的尾
// ...
};
运用start、finish、end_of_storage三个迭代器,便可轻易地提供首尾标示、大小等机能。
cpp
class vector
{
// ...
public:
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); }
reference front() { return *begin(); }
reference back() { return *(end() - 1); }
// ...
};

vector的构造和内存管理
vector提供许多constructors,其中一个允许我们指定空间大小及初值。
cpp
// 构造函数,允许指定vector大小n和初值value
vector(size_type n, const T& value) { fill_initialize(n, value); }
// 填充并予以初始化
void fill_initialize(size_type n, const T& value)
{
start = allocate_and_fill(n, value);
finish = start + n;
end_of_storage = finish;
}
// 配置而后填充
iterator allocate_and_fill(size_type n, const T& x)
{
iterator result = data_allocator::allocate(n); // 配置n个元素空间
uninitialized_fill_n(result, n, x);
return result;
}
当push_back()
将新元素插入于vector尾端时,该函数首先检查是否还有备用空间,如果有就直接在备用空间上构造元素,并调整迭代器finish,使vector变大;如果没有备用空间就扩充空间(2倍扩容)。
cpp
void push_back(const T& x)
{
if (finish != end_of_storage)
{
// 有备用空间
construct(finish, x);
++finish; // 调整水位高度
}
else
{
// 无备用空间,需要扩容
insert_aux(end(), x);
}
}
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);
*positioon = x_copy;
}
else
{
// 已无备用空间
const size_type old_size = size();
// 若原大小为0,则配置1个元素大小
// 若原大小不为0,则二倍扩容
const size_type len = old_size != 0 ? 2 * old_size : 1;
iterator new_start = data_allocator::allocate(len); // 实际配置
construct(new_finish, x); // 为新元素设定初值x
++new_finish;
// 将原vector的备用空间中的内容也拷贝过来(?)
new_finish = uninitialized_copy(position, finish, new_finish);
}
catch(...)
{
destroy(new_start, new_finish);
data_allocator::deallocate(new_start, len);
throw;
}
// 析构并释放原vector
destroy(begin(), end());
deallocate();
// 调整迭代器,指向新vector
start = new_start;
finish = new_finish;
end_of_storage = new_start + len;
}
- 所谓动态增加大小,并不是在原空间之后连续新空间(因为无法保证原空间之后尚有可配置的空间),而是以原大小的两倍另外配置一块较大空间,然后将原内容拷贝过来,然后才开始在原内容之后构造新元素,并释放原空间。
list
- 相比于vector的连续线性空间,list就显得复杂许多。**它的好处是每次插入或删除一个元素,就配置或释放一个元素空间。**list对于空间的运用绝对精准,一点也不浪费。
list的节点
cpp
// 实质是一个双向链表
template <class T>
struct __list_node
{
typedef void* void_pointer;
void_pointer prev; // 型别为void*
void_pointer next;
T data;
};
- list不能用普通指针作为迭代器,因为其节点不保证在存储空间中连续存在。
list的数据结构
SGI list不仅是一个双向链表,而且还是一个环状双向链表。(只需一个指针,便可以完整表现整个链表。)
cpp
template <class T, class Alloc = alloc>
class list
{
protected:
typedef __list_node<T> list_node;
public:
typedef list_node* link_type;
protected:
link_type node; // 只要一个指针,便可表示整个环状双向链表
};

deque
- vector是单向开口的连续线性空间,deque则是双向开口的连续线性空间。
- deque允许常数时间内对头端进行元素的插入与移除操作。
- deque没有所谓容量(capacity)观念,它是动态地分段连续空间组合而成。
- 除非必要,应尽可能选用vector而非deque。

deque逻辑上是连续空间,实际是由一段一段的定量连续空间构成。一旦有必要在deque的前端或尾端增加新空间,便配置一段定量连续空间,串接在整个deque的头端或尾端。无需维护整体连续,但代价是复杂的迭代器架构。

deque的数据结构
deque的迭代器(较为复杂)必须能够指出分段连续空间(亦即缓冲区)在哪里;必须能够判断自己是否已经处于其所在缓冲区的边缘,如果是,一旦前进或后退时就必须跳跃至下一个或上一个缓冲区。
cpp
template <class T, class Alloc = alloc, size_t BufSiz = 0>
class deque
{
public:
typedef T value_type;
typedef value_type* pointer;
typedef size_t size_type;
public:
typedef __deque_iterator<T, T&, T*, BufSiz> iterator;
protected:
typedef pointer* map_pointer;
protected:
iterator start; // 第一个节点
iterator finish; // 最后一个节点
map_pointer map; // 指向map
size_type map_size; // map内有多少指针
};
stack
- stack是一种"先进后出"(FILO)的数据结构。
- stack除了最顶端外,没有任何其它方法可以存取stack的其它元素。换言之,stack不允许所有遍历行为。
- stack不提供走访功能,也不提供迭代器。

stack的实现
stack以底部容器完成其所有工作,因此STL stack往往不被归类为container,而被归类为container adapter。
cpp
template <class T, class Sequence = deque<T>>
class stack
{
// ...
public:
typedef typename Sequence::value_type value_type;
typedef typename Sequence::size_type size_type;
typedef typename Sequence::reference reference;
typedef typename Sequence::const_reference const_reference;
protected:
Sequence c; // 底层容器
public:
// 完全利用Sequence c的操作,完成stack的操作
bool empty() const { return c.empty(); }
size_type size() const { return c.size(); }
reference top() { return c.back(); }
const_reference top() const { return c.back(); }
void push(const value_type& x) { c.push_back(x); }
void pop() { c.pop_back(); }
};
queue
- queue是一种"先进先出"(FIFO)的数据结构。
- queue除了最顶端可以取出、最低端可以加入外,没有任何其它方法可以存取queue的其它元素。换言之,queue不允许所有遍历行为。
- queue亦不提供迭代器。

queue的实现
queue以底部容器完成其所有工作,因此STL queue往往不被归类为container,而被归类为container adapter。
cpp
template <class T, class Sequence = deque<T>>
class queue
{
// ...
public:
typedef typename Sequence::value_type value_type;
typedef typename Sequence::size_type size_type;
typedef typename Sequence::reference reference;
typedef typename Sequence::const_reference const_reference;
protected:
Sequence c; // 底层容器
public:
// 完全利用Sequence c的操作,完成queue的操作
bool empty() const { return c.empty(); }
size_type size() const { return c.size(); }
reference front() { return c.front(); }
const_reference front() const { return c.front(); }
reference back() { return c.back(); }
const_reference back() const { return c.back(); }
void push(const value_type& x) { c.push_back(x); }
void pop() { c.pop_front(); }
};
priority_queue
- 拥有权值观念的queue,其内的元素并非按照被推入的次序排列,而是自动依照元素的权值排列。
- priority_queue的所有元素,进出都有一定的规则,只有queue顶端的元素才有机会被外界取用。
priority_queue的实现
cpp
template <class T, class Sequence = vector<T>,
class Compare = less<typename Sequence::value_type> >
class priority_queue
{
public:
typedef typename Sequence::value_type value_type;
typedef typename Sequence::size_type size_type;
typedef typename Sequence::reference reference;
typedef typename Sequence::const_reference const_reference;
protected:
Sequence c; // 底层容器
Compare comp; // 元素大小比较标准
public:
priority_queue() : c() {}
explicit priority_queue(const Compare& x) : c(), comp(x) {}
template <class InputIterator>
priority_queue(InputIterator first, InputIterator last, const Compare& x) : c(first, last), comp(x) { make_heap(c.begin(), c.end(), comp); }
template <class InputIterator>
priority_queue(InputIterator first, InputIterator last) : c(first, last) { make_heap(c.begin(), c.end(), comp); }
bool empty() const { return c.empty(); }
size_type size() const { return c.size(); }
const_reference top() const {return c.front(); }
void push(const value_type& x)
{
// 将新元素推入末端
c.push_back(x);
// 重排heap
push_heap(c.begin(), c.end(), comp);
}
void pop()
{
pop_heap(c.begin(), c.end(), comp);
c.pop_back();
}
};
map和set
map属于关联容器与序列容器有着根本性的不同,序列容器的元素是按照在容器中的位置来顺序保存和访问的,而关联容器的元素是按关键元素来保存和访问的,都是基于红黑树实现的。
- 每个节点不是红色就是黑色。
- 根节点为黑色。
- 如果节点为红,其子节点必须为黑。
- 任一节点至NULL(树尾端)的任何路径,所含之黑节点数必须相同。
