STL源码剖析—顺序容器
一、vector
1、vector简介:
vector的数据安排及其操作方式与数组非常相似,微小的差别在于空间的使用,数组是静态空间,一旦配置了就不能改变。vector是动态空间,随着元素的加入,它的内部机制会自行扩充空间以容纳新元素。
2、vector的构造和内存管理
其数据结构为:
class vector
{
...
private:
iterator start; //表示目前使用空间的头
iterator finish; //表示目前使用空间的尾
iterator end_of_storage; //表示目前可用空间的尾
...
};
vector提供许多的constructs,其中一个允许我们指定空间大小及初值。
vector(size_type n, const T& value) //构造函数,允许指定vector大小n和初值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 reslut = data_allocator::allocator(n);//配置n个元素空间
uninitialized_fill_n(result, n, x); //全局函数
return result;
}//uninitialized_fill_n()根据第一参数的型别特性决定使用fill_n()或反复调用construct()来完成任务,
//如果[first,n)范围内的每一个迭代器都指向未初始化的内存,那么uninitialized_fill_n()会调用copy construct,在该范围内的产生x的复制品,
//也就是在该范围内的每个迭代器都会调用construct(&*i,x),在对应位置上产生x的复制品.
当我们以push_back()将新元素插入vector尾端时,该函数首先检查是否还有备用空间,如果有就直接在备用空间上构造元素,并调整迭代器finish,使vector变大,如果没有备用空间了,就扩充空间,扩充空间的过程包括重新配置、移动数据和释放原空间。
注:当没有备用空间时,为了时间成本,容量会扩充至两倍,如果两倍容量仍不足,就扩充至足够大的容量。另外,对vector的任何操作,一旦引起空间重新配置,指向原vector的所有迭代器就失效了。
void push_back(const T& x)
{
if (finish != end_of_storage) //还有备用空间
{
construct(finish, x); //构造元素
++finish;
}
else
{
insert_aux(end(), x); //如果没有备用空间,调用该全局函数
}
}
insert_aux(iterator position, const T& x)
{
if (finish != end_of_storage) //还有备用空间,
{
construct(finish, *(finish - )); //在备用空间起始处构造一个元素,并以vector最后一个元素值为其初始值
++finish;
T x_copy = x;
copy_backward(position, finish - , finish - );
*position = x_copy;
}
else //没有备用空间
{
const size_type old_size = size();
const size_type len = old_size != ? * old_size : ;
//配置原则:如果原大小为0,则配置一个元素大小空间
//如果原大小不为0,则配置为原大小的两倍,
//前半段用来放置原数据,后半段准备用来放新数据
iterator new_start = data_allocator::allocator(len); //配置大小为len的元素空间
iterator new_finish = new_start;
try
{
new_finish = uninitialized_copy(start, position, new_start); //将原vector的内容拷贝到新vector中
construct(new_finish, x); //为新元素设定初值x;
++finish;
new_finish = uninitialized_copy(position, finish, new_finish);//将安插点的原内容拷贝过来
}
catch (...)
{
destory(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;
}
}
3、vector的元素操作
vector所提供的元素很多,见下图
这里只选择几种典型的操作进行介绍。详细参考http://www.cplusplus.com/reference/vector/vector/?kw=vector。
iterator erase(iterator first, iterator last) //清除[first,last)中所有的元素
{
iterator i = copy(last, finish, first);
destroy(i, finish);
finish = finish - (last - first);
return first;
} iterator erase(iterator position) //清除某个位置上的元素
{
if (position + != end())
{
copy(position + , finish, position); //该函数将[position + 1,finish)的元素拷贝到从position开始的位置上
}
--finish;
destroy(finish);
return position;
}
二、list
1、list简介
相对vector的连续性空间,list则为离散的空间布局,每次插入或删除一个元素,就会配置或释放一个元素空间。
2、list的构造和内存管理
SGI list是一个环状双向链表,其节点结构为:
template <class T>
struct _list_node
{
typedef void* void_pointer;
void_pointer prve;
void_pointer next;
T data;
};
以下四个函数,分别用来配置、释放、构造、销毁一个节点:
link_type get_node() //配置一个节点
{
return list_node_allocator::allocator();
}
void put_node(link_type p) //释放一个节点
{
list_node_allocator::deallocate(p);
}
link_type create_node(const T& x) //产生一个节点,带有元素值
{
link_type p = get_node();
construct(&p->data, x);
return p;
}
void destroy_node(link_type p) //销毁(析构释放)一个节点
{
destroy(&p->data);
put_node(p);
}
void empty_initialize() //产生一个空链表
{
node = get_node();
node->next = node; //令头尾都指向自己
node->prve = node;
}
3、list的元素操作
主要的操作包括:
iterator insert(iterator position, const T& x) //在迭代器position所指位置出插入一个节点,内容为x
{
link_type tmp = create_node(x);
tmp->next = position.node;
tmp->prve = position.node->prve;
(link_type(position.node->prve))->next = tmp;
position.node->prve = tmp;
return tmp;
} iterator erase(iterator position) //移除迭代器position所指节点
{
link_type next_node = link_type(position.node->next);
link_type prve_node = link_type(position.node->prve);
prve_node->next = next_node;
next_node->prve = prve_node;
destroy_node(position.node);
return iterator(next_node);
} void remove(const T& value)//将数值为value的元素移除
{
iterator first = begin();
iterator last = end();
while (first != last)
{
iterator next = last;
++next;
if (*first == value)
erase(first);
first = next;
}
}
list还提供一个迁移操作,将某连续范围的元素迁移到某个特定位置之前,这个操作为其他人如splice、sort、merge奠定了基础。
void transfer(iterator position, iterator first, iterator last) //将[first,last)中的元素移到position之前
{
if (position != last)
{
(*(link_type((*last.node).prve))).next = position.node;
(*(link_type((*first.node).prve))).next = position.node;
(*(link_type((*position.node).prve))).next = first.node;
link_type tmp = link_type((*position.node).prve);
(*position.ndoe).prve = (*last.node).prve;
(*last.node).prve = (*first.node).prve;
(*last.node).prve = tmp;
}
}
三、deque
1、deque的简介
deque是一种双向开口的连续性空间,可以在头尾两端分别做元素的插入和删除操作。
2、deque的构造和内存管理
deuqe除了维护一个指向map的指针,也维护start、finish两个迭代器,分别指向第一缓冲区的第一个元素和最后一个缓冲区的最后一个元素(的下一个位置)。
template<class T,class Alloc=alloc,size_t BufSiz=>
class deque
{
public :
typedef T value_type;
typedef value_type* pointer;
typedef size_t size_type;
typedef _deque_iterator<T, T&, T*, BufSiz> iterator;
protected:
typedef pointer* map_pointer;
iterator start;
iterator finish;
map_pointer mao;
size_type map_size;
}
根据上述结构,容易得到下列函数:
iterator begin()
{
return start;
}
iterator end()
{
return finish;
}
size_type size() const
{
return finish - start;
}
bool empty() const
{
return finish == start;
}
reference back()
{
iterator tmp = finish;
--tmp;
return *tmp;
}
reference operator[](size_type n) //对[]符号的重载
{
return start[difference_type(n)];
}
STL源码剖析—顺序容器的更多相关文章
- STL源码剖析之序列式容器
最近由于找工作需要,准备深入学习一下STL源码,我看的是侯捷所著的<STL源码剖析>.之所以看这本书主要是由于我过去曾经接触过一些台湾人,我一直觉得台湾人非常不错(这里不涉及任何政治,仅限 ...
- 《STL源码剖析》读书笔记
转载:https://www.cnblogs.com/xiaoyi115/p/3721922.html 直接逼入正题. Standard Template Library简称STL.STL可分为容器( ...
- 通读《STL源码剖析》之后的一点读书笔记
直接逼入正题. Standard Template Library简称STL.STL可分为容器(containers).迭代器(iterators).空间配置器(allocator).配接器(adap ...
- 【STL 源码剖析】浅谈 STL 迭代器与 traits 编程技法
大家好,我是小贺. 点赞再看,养成习惯 文章每周持续更新,可以微信搜索「herongwei」第一时间阅读和催更,本文 GitHub : https://github.com/rongweihe/Mor ...
- STL"源码"剖析-重点知识总结
STL是C++重要的组件之一,大学时看过<STL源码剖析>这本书,这几天复习了一下,总结出以下LZ认为比较重要的知识点,内容有点略多 :) 1.STL概述 STL提供六大组件,彼此可以组合 ...
- 【转载】STL"源码"剖析-重点知识总结
原文:STL"源码"剖析-重点知识总结 STL是C++重要的组件之一,大学时看过<STL源码剖析>这本书,这几天复习了一下,总结出以下LZ认为比较重要的知识点,内容有点 ...
- (原创滴~)STL源码剖析读书总结1——GP和内存管理
读完侯捷先生的<STL源码剖析>,感觉真如他本人所说的"庖丁解牛,恢恢乎游刃有余",STL底层的实现一览无余,给人一种自己的C++水平又提升了一个level的幻觉,呵呵 ...
- STL源码剖析读书笔记之vector
STL源码剖析读书笔记之vector 1.vector概述 vector是一种序列式容器,我的理解是vector就像数组.但是数组有一个很大的问题就是当我们分配 一个一定大小的数组的时候,起初也许我们 ...
- STL源码剖析 迭代器(iterator)概念与编程技法(三)
1 STL迭代器原理 1.1 迭代器(iterator)是一中检查容器内元素并遍历元素的数据类型,STL设计的精髓在于,把容器(Containers)和算法(Algorithms)分开,而迭代器(i ...
随机推荐
- 【转】【Python】装饰器
1.闭包 >>> def outer(): ... x = 1 ... def inner(): ... ... return inner >>> foo = ou ...
- Wifi 评分机制分析
从android N开始,引入了wifi评分机制,选择wifi的时候会通过评分来选择. android O源码 frameworks\opt\net\wifi\service\java\com\and ...
- Linux中几个与文档相关的命令
一.介绍 本文将介绍几个与文档相关的命令 软件环境: 物理机 Windows 8.0 虚拟机 VMware Workstation 12 Linux系统 CentOS 7.3 二.命令cat 命令ca ...
- Java如何查看线程的优先级?
Java编程中,如何查看线程的优先级? 以下示例演示如何使用Thread类的getPriority()方法检查线程的优先级. package com.yiibai; public class Thre ...
- e770. 确定按钮租中已选的单选按钮
When you ask a button group for the currently selected radio button, it returns the selected radio b ...
- 高校区LAN局域网校内网组建实践经验
项目描述: ●校区计算机网络组建与管理和维护. 主要内容: 1.电脑故障诊断与排除与维护. 2.修复局域网内的故障电脑. 3.局域网架设虚拟系统. 4.局域网升级. 5.局域网基础架构. 6.电脑系统 ...
- Unity 基础-------------------------关于Anchor锚点的理解
Unity进阶技巧 - RectTransform详解 Zui 关注 2016.02.17 01:27 字数 1704 阅读 22157评论 13喜欢 57赞赏 2 RectTransform属性一览 ...
- 创建 React-Native 工程时,如何指定特定的 React-Native 版本
react-native 可能会出现一种情况,就是版本最高的可能出现有些东西不太稳定,这时候要用到旧的版本怎么办?就可以用以下方法创建项目. 0. 原因 创建新的 React-Native (以下简称 ...
- spark shell学习笔记
http://homepage.cs.latrobe.edu.au/zhe/ZhenHeSparkRDDAPIExamples.html
- MongoDB之update
Update操作只作用于集合中存在的文档.MongoDB提供了如下方法来更新集合中的文档: db.collection.update() db.collection.updateOne() New i ...