概述
VS2010的源码真的让人放弃,还是安安稳稳看侯捷老师的SGI
源码(SGI vector)
实现框架
#include<iostream>
#include<cstddef>//用到了size_t和ptrdiff_t
using namespace std;
template<class T,class Alloc=alloc>
class vector
{};
(1) 空间分配属性,由于是对外封闭的,所以protected
定义一个空间分配器,当然要有空间的起始地址和存储结束地址及最大存储结束地址,以及初始化的函数和释放空间的函数
protected:
typedef simple_alloc<value_type,Alloc>data_allocator;//定义一个空间分配器
iterator start;//起始地址
iterator finish;//容器实际大小的末端
iterator end_of_storage;//容器最大可容大小的末端
void fill_initialize(size_type n,const T&x);//分配n个元素的大小,初值为x
void deallocate();//释放整个容器空间
(2) 共有访问的属性,可供外部用户访问, 定义为public
/*******公有属性,类型别名************/
typedef T value_type;//元素类型
typedef value_type* pointer;//指针类型
typedef value_type* iterator;//迭代器类型
typedef const value_type* const_iterator;//指向常量的迭代器类型
typedef value_type& reference;//引用类型
typedef size_t size_type;//大小类型
typedef ptrdiff_t difference_type;//指针差值类型
(3) 容器的构造函数和析构函数
注意带一个参数的构造函数注意要用关键字explicit
,原因见上篇
/***********构造/析构函数*************/
vector();//默认构造函数
explicit vector(size_type n);//单形参构造函数,explicit防止类型的隐式转化
vector(size_t n,const T & t);//带初值的构造函数
~vector();//析构函数
(4)插入操作
/***********插入操作******************/
void push_back(const T& t);//后插入值t
iterator insert(iterator iter,const T& t);//在iter前插入值t
void insert(iterator iter,size_type n,const T& t);//在iter前插入n个初值为t的元素
void insert(iterator iter,iterator b,iterator e);//在iter前插入迭代器范围[b,e)的元素
(5)删除操作
/***********删除操作******************/
iterator erase(iterator iter);//删除iter所指向的元素
iterator erase(iterator b,iterator e);//删除迭代器范围[b,e)所指向的元素
void clear();//清除容器内所有的元素
void pop_back();//将容器的最后一个元素弹出
(6)大小操作
max_size()
是获取编译器可申请的最大空间
/***********大小操作******************/
size_type size()const;//获取实际使用的大小
size_type max_size()const;//获取编译器可申请的最大大小,以元素的个数为单位
size_type capacity()const;//获取容器当前的容量
bool empty()const;//是否为空容器
void resize(size_type n);//将容器的实际大小调整为n
void resize(size_type n,const T& t);//将容器的时间大小调整为n,并提供必要的初值t
(7)访问操作
end()
是取容器最后一个元素的下一个指针
/***********访问操作******************/
iterator begin();//取容器头指针
iterator end();//取容器最后一个元素的下一个指针
reference operator[](size_type i);//v[i]
reference at(size_type i);//取容器的第i个元素,等价于v[i]
reference front();//返回容器头部元素的值
refrence back();//返回容器尾部元素的值
具体实现
1,空间分配器
用data_allocator
进行内存空间的分配,用data_allocator:deallocate()
进行释放,初始化的时候不多分配内存,内存空间结束点和已经使用的内存空间结束点是相同的
allocate_and_fill
是发配空间,并且复制对象到分配的空间处
protected:
// 这个提供STL标准的allocator接口
typedef simple_alloc <value_type, Alloc> data_allocator;
iterator start; // 表示目前使用空间的头
iterator finish; // 表示目前使用空间的尾
iterator end_of_storage; // 表示实际分配内存空间的尾
void fill_initialize(size_type n, const T& value)//初始化
{
start = allocate_and_fill(n, value);
finish = start + n; // 设置当前使用内存空间的结束点
// 构造阶段, 此实作不多分配内存,
// 所以要设置内存空间结束点和已经使用的内存空间结束点相同
end_of_storage = finish;
}
protected:
// 分配空间, 并且复制对象到分配的空间处
iterator allocate_and_fill(size_type n, const T& x)
{
iterator result = data_allocator::allocate(n);
uninitialized_fill_n(result, n, x);
return result;
}
// 释放分配的内存空间
void deallocate()
{
// 由于使用的是data_allocator进行内存空间的分配,
// 所以需要同样使用data_allocator::deallocate()进行释放
// 如果直接释放, 对于data_allocator内部使用内存池的版本
// 就会发生错误
if (start)
data_allocator::deallocate(start, end_of_storage - start);
}
(2) 共有访问的属性,可供外部用户访问
public:
// 获取几种迭代器
iterator begin() { return start; }
iterator end() { return finish; }
// 返回当前对象个数
size_type size() const { return size_type(end() - begin()); }
size_type max_size() const { return size_type(-1) / sizeof(T); }
// 返回重新分配内存前最多能存储的对象个数
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); }
(3)构造函数和析构函数
默认构造函数不分配内存空间
单参数构造函数需要对象提供默认构造函数
析构函数需要首先析构对象destory()
,然后释放内存deallocate()
重载等号操作符来构造函数其底层用的就是拷贝构造函数
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()); }
vector(const vector<T, Alloc>& x)//拷贝构造函数?将x的拷贝到当前容器中
{
start = allocate_and_copy(x.end() - x.begin(), x.begin(), x.end());
finish = start + (x.end() - x.begin());
end_of_storage = finish;
}
~vector()
{
// 析构对象
destroy(start, finish);
// 释放内存
deallocate();
}
vector<T, Alloc>& operator=(const vector<T, Alloc>& x);//还可以重载等号操作符进行构造初始化,用拷贝构造函数作为底层
(4) 插入操作
在插入元素之前需要先判断内存是否已满,如果还没有则直接加,否则的话需要用insert_aux()
重新分配内存为原来的2倍,它会首先判断内存是否够用,像在函数指定位置插入元素时就没有在本体函数内部判断内存是否够,而是到insert_aux()
中再去做的判断
void insert_aux(iterator position, const T& x);//它还会再中心判断是否内存够用
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;
}
⭐⭐⭐insert_aux()
insert_aux 在指定位置插入一个元素
- 首先判断是否还有空间,如果有,从opsition开始, 整体向后移动一个位置,
- 否则没有备用空间的话,
2.1. 先看当前内存容量,如果当前没有内存是0,因为vector默认构造函数是不分配内存段的,分配len = 1;如果当前内存不为0,则分配len = 原来的2倍
2.2. 用data_allocator进行分配len的长度,vector的每次扩容数据都是要进行搬迁的,所以一开始new_finish = new_start;
2.3. 首先将原vector的安插点以前的内容拷贝到新vector,将新元素放上position,再搬剩下的元素;
2.3. 扩容复制完之后要删除原空间的元素释放原空间内存; - 代码中还有回滚操作,那就是释放新范围的元素再释放内存空间即可
template <class T, class Alloc>
void insert_aux(iterator position, const T& x)
{
if (finish != end_of_storage) // 还有备用空间
{
// 在备用空间起始处构造一个元素,并以vector最后一个元素值为其初值
construct(finish, *(finish - 1));//[start,finish)
++finish;//扩容完成
T x_copy = x;
copy_backward(position, finish - 2, finish - 1);//从opsition开始, 整体向后移动一个位置
*position = x_copy;
}
else // 已无备用空间
{
const size_type old_size = size();
const size_type len = old_size != 0 ? 2 * old_size : 1;
// 以上配置元素:如果大小为0,则配置1(个元素大小)
// 如果大小不为0,则配置原来大小的两倍
// 前半段用来放置原数据,后半段准备用来放置新数据
iterator new_start = data_allocator::allocate(len); // 实际配置
iterator new_finish = new_start;
// 将内存重新配置
try
{
// 将原vector的安插点以前的内容拷贝到新vector
new_finish = uninitialized_copy(start, position, new_start);
// 为新元素设定初值 x
construct(new_finish, x);
// 调整水位
++new_finish;
// 将安插点以后的原内容也拷贝过来
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;
}
}
insert_aux在指定位置插入n个元素
- 首先判断n是否等于0,等于0不做任何操作; 否则判断内存空间是否足够,
- 空间够的话
2.1. 根据要插入数的个数和[position, finish)元素即待后移元素个数多少不进痛行不同的指针移动,具体方法画图就能明了:
2.2 如果待插入元素少,先将最后n个元素后移到finish后,这一步目的就是为了增加n个元素空间,再将position后元素后移,最后插入;
2.3 如果待插入元素多,那一定会占据完剩余的空间,并且就n来说还需要n-elem_after个元素,现在position后的元素一定在新插入元素之后,所以先增加差值个元素,再在之后将position后的元素移动,方法非常巧妙,可学习 - 空间不够的话:
3.1. 首先决定新长度:就长度的两倍 , 或旧长度+新增元素个数
3.2 用data_allocator::allocate(len)配置空间,还是一样,首先将旧的vector的插入点之前的元素复制到新空间,再将新增元素(初值皆为n)填入新空间,最后将旧vector的插入点之后的元素复制到新空间
3.3移动完之后要将原来的元素删除,将原来的空间释放,并将迭代器重新指向
3.4 回滚同理
template <class T, class Alloc>
template <class T, class Alloc>
void insert(iterator position, size_type n, const T& x)
{
// 如果n为0则不进行任何操作
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; // 将vector 尾端标记后移
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);
// 以下配置新的vector空间
iterator new_start = data_allocator::allocate(len);
iterator new_finish = new_start;
__STL_TRY
{
// 首先将旧的vector的插入点之前的元素复制到新空间
new_finish = uninitialized_copy(start, position, new_start);
// 再将新增元素(初值皆为n)填入新空间
new_finish = uninitialized_fill_n(new_finish, n, x);
// 再将旧vector的插入点之后的元素复制到新空间
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;
}
}
}
(5)删除操作
iterator erase(iterator first, iterator last)
{
iterator i = copy(last, finish, first);//删除first到last就意味着将last到finish的值拷贝到first √
destroy(i, finish);// 析构掉需要析构的元素
finish = finish - (last - first);
return first;
}
void clear() { erase(begin(), end()); }
(6)大小操作
// 调整size, 但是并不会重新分配内存空间
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()); }
(7)访问操作
reference front() { return *begin(); }
reference back() { return *(end() - 1); }
以上部分参考并感谢https://blog.csdn.net/jxh_123/category_2333293.html
vector容器概述
- vector是动态数组,与之对应的是array静态数组,array是静态空间,空间大小一旦配置了就不能改变;但vector如果想改变(变大或变小)都是可以做到的:
改变过程: 1,首先配置一块新空间; 2,将元素从现在的地址一一复制到新地址; 3,再将原来空间空间释放
至于什么时候进行扩大或缩小,vector内部会自动实现, 因此vector的实现技术关键就在于对其空间大小的控制以及重新配置时的数据移动效率 - vector维护的是一个连续性空间,所以vector支持随机存取
- 注意当vector动态增加大小时,并不是在原空间之后扩展新空间,因此,对vector的任何操作,一旦引起空间重新配置,指向原来vector的所有迭代器就都失效了
vector的基本函数实现
1, 构造函数
- vector():创建一个空vector
- vector(int nSize):创建一个vector,元素个数为nSize
- vector(int nSize, const t&t):创建一个vector,元素个数为nSize, 且均值为t
- vector(const vector&):复制构造函数
- vector(begin, end);复制这个范围内另一个数组元素到vector
2, 增加函数
- void push_back(const T& x);
- iterator insert(iterator it,const T& x):向量中迭代器指向元素前增加一个元素x
- iterator insert(iterator it,int n,const T& x):向量中迭代器指向元素前增加n个相同的元素x
- iterator insert(iterator it,const_iterator first,const_iterator last):向量中迭代器指向元素前插入另一个相同类型向量的[first,last)间的数据
删除元素
- iterator erase(iterator it):删除向量中迭代器指向元素
- iterator erase(iterator first,iterator last):删除向量中[first,last)中元素
- void pop_back():删除向量中最后一个元素
- void clear():清空向量中所有元素
遍历函数
- reference at(int pos):返回pos位置元素的引用
- reference front():返回首元素的引用
- reference back():返回尾元素的引用
- iterator begin():返回向量头指针,指向第一个元素
- iterator end(): 返回向量尾指针,指向向量最后一个元素的下一个位置
- reverse_iterator rbegin():反向迭代器,指向最后一个元素
- reverse_iterator rend():反向迭代器,指向第一个元素之前的位置
判断函数
- bool empty() const:判断向量是否为空,若为空,则向量中无元素
大小函数
- int size() const:返回向量中元素的个数
- int capacity() const:返回当前向量所能容纳的最大元素值
- int max_size() const:返回最大可允许的vector元素数量值
其他函数
- void swap(vector&):交换两个同类型向量的数据,与另一个vector交换数据
- void assign(int n,const T& x):设置向量中第n个元素的值为x
- void assign(const_iterator first,const_iterator last):向量中[first,last)中元素设置成当前向量元素
妈欸……开心,终于迈出了第一步
最后
以上就是务实雪糕为你收集整理的STLvector源码——实现框架、具体实现的详细分段剖析(重点是insert_aux在指定位置插入元素和在指定位置插入n个元素的源码)、vector实现的基本函数总结的全部内容,希望文章能够帮你解决STLvector源码——实现框架、具体实现的详细分段剖析(重点是insert_aux在指定位置插入元素和在指定位置插入n个元素的源码)、vector实现的基本函数总结所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复