STL浅析——序列式容器vector的构造和内存管理: constructor() 和 push_back()
Posted Forever-Road
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了STL浅析——序列式容器vector的构造和内存管理: constructor() 和 push_back()相关的知识,希望对你有一定的参考价值。
咱们先来做一个测试capacity是容器容量,size是大小:
#include <iostream> #include <vector> using namespace std; int main(){ vector<int> result; for (int i = 0; i < 17; i++) { result.push_back(i); printf("element count: %d\\t", result.size()); printf("capacity: %d\\t", result.capacity()); printf("first element\'s address: %x\\n", result.begin()); } return 0; }
运行结果:
可以观察到每次容器满了需要扩容的时候,容量总是呈现两倍增长,而且每次扩容,容器第一个元素所在地址都会发生改变,由此我们知道,容器的扩容时实际是另外寻找一片更大的空间,VS的如下:
扩容的倍数不一样VS为1.5倍扩容,最好的扩容倍数是黄金分割比,1.618倍,当然也不可能那么精确。。。
vector 缺省使用 alloc (实际上是 宏__STL_DEFAULT_ALLOCATOR(_Tp) 的typedef,但是该宏本质还是第二级空间配置器最终封装而成的 alloc) 作为空间配置器,并据此定义了一个 _Base。
class vector : protected _Vector_base<_Tp, _Alloc> { ... private: typedef _Vector_base<_Tp, _Alloc> _Base; ...
//以前是:
typedef simple_alloc<value_type, Alloc> data_allocator;
//最新版本是进行了进一步封装,分开了基本参数和泛型操作函数
}
vector 提供了许多constructors,其中一个允许我们指定空间大小与初值:
//就是这个,允许我们指定空间大小和初值
vector(size_type __n, const _Tp& __value, const allocator_type& __a = allocator_type()) : _Base(__n, __a) { _M_finish = uninitialized_fill_n(_M_start, __n, __value); } //上面 uninitialized_fill_n 这个函数,藏得很深在stl_uninitialized.h里面,实际该函数还是对fill函数的一次封装
//最终调用的是fill函数,在 stl_alogbase.h 这个头文件下面,如下,
//不过这个文件夹下面有很多fill函数,我想应该是uninitialized_fill_n的作用还包括型别判断,根据型别不同调用不同的fill函数
void fill(_ForwardIter __first, _ForwardIter __last, const _Tp& __value) { __STL_REQUIRES(_ForwardIter, _Mutable_ForwardIterator); for ( ; __first != __last; ++__first) *__first = __value; }
我们以 push_back() 函数将新元素插入vector尾端时,该函数首先检查是否还有备用空间,如果有则直接在备用空间上构造元素,并调整迭代器finish,使得vector变大。如果没有备用空间就扩充:
void push_back(const _Tp& __x) { if (_M_finish != _M_end_of_storage) { construct(_M_finish, __x); //直接placement构造元素,移动_M_finish指针 ++_M_finish; } else _M_insert_aux(end(), __x); //重新构造整个vector并插入元素 }
//表示不懂为啥要重载一次push_back()。。。 void push_back() { if (_M_finish != _M_end_of_storage) { construct(_M_finish); ++_M_finish; } else _M_insert_aux(end()); }
至于 _M_insert_aux 函数,有点复杂,但是也不难理解,如下:
vector<_Tp, _Alloc>::_M_insert_aux(iterator __position, const _Tp& __x) { if (_M_finish != _M_end_of_storage) //有备用空间
{
//在备用空间起始处构造一个元素,并以vector最后一个元素为其初始值 construct(_M_finish, *(_M_finish - 1)); ++_M_finish; //调整水位 _Tp __x_copy = __x; copy_backward(__position, _M_finish - 2, _M_finish - 1); *__position = __x_copy; } else //无备用空间
{ const size_type __old_size = size(); const size_type __len = __old_size != 0 ? 2 * __old_size : 1; //以上配置原则,如果原大小为0,则配置一个元素大小
//如果原大小不为零,则配置原来大小的两倍
//前半段用来存放原始数据,后半段用来存放新数据
iterator __new_start = _M_allocate(__len); iterator __new_finish = __new_start; __STL_TRY
{
//原vector内容拷贝到新vector __new_finish = uninitialized_copy(_M_start, __position, __new_start); construct(__new_finish, __x); //为新元素设定初始x ++__new_finish; //调整水位
//安插点的原内容也一起拷贝 __new_finish = uninitialized_copy(__position, _M_finish, __new_finish); } __STL_UNWIND((destroy(__new_start,__new_finish), _M_deallocate(__new_start,__len)));
//销毁,释放原vector destroy(begin(), end()); _M_deallocate(_M_start, _M_end_of_storage - _M_start);
//调整迭代器,指向新的vector _M_start = __new_start; _M_finish = __new_finish; _M_end_of_storage = __new_start + __len; } }
因此可以明白,对 vector 的任何操作,一旦引起空间重新配置,则指向原 vector 的所有迭代器都会失效。
以上是关于STL浅析——序列式容器vector的构造和内存管理: constructor() 和 push_back()的主要内容,如果未能解决你的问题,请参考以下文章