vector作为实际项目用的非常频繁的容器,你了解它的内部实现吗?
比如vector<int>::itertor iter
1、iter到底是什么呢?
2、push_back到底发生什么了?
3、erase或者clear的时候内存有没有被释放?
如果这些问题你已经知道,可以不用看这篇文章了。
本文参考的版本是SGI for GCC。
大家都知道STL是泛型编程的典范,vector当然也是,有码有真相:
template <class _Tp, class _Alloc = __STL_DEFAULT_ALLOCATOR(_Tp) > class vector : protected _Vector_base<_Tp, _Alloc> { //此处省略N多字 }
第一个问题,很简单,从源码就可以看到:
typedef const value_type* const_pointer; typedef value_type* iterator; typedef const value_type* const_iterator;
2、push_back发生了什么呢?
当然vector在构造时,会预先分配写内存:
当然要看push_back的实现了:
void push_back(const _Tp& __x) { if (_M_finish != _M_end_of_storage) { construct(_M_finish, __x); ++_M_finish; } else _M_insert_aux(end(), __x); }
如果当前还有可用内存,那就直接用placement new构造元素(不知道的google placement new,其实很简单就是在给定内存上构造元素);
否则,就_M_insert_aux,这个函数体有点小复杂,但是是重点(插一句:古语有言:莫将容易得,却坐等闲看。):
记住我们要往最后一个实际当前无效的内存上去插入元素,因为end()肯定是无效啦。
好,现在看下代码注释版:
template <class _Tp, class _Alloc> void vector<_Tp, _Alloc>::_M_insert_aux(iterator __position, const _Tp& __x) { if (_M_finish != _M_end_of_storage) { //如果当前内存池还有内存 construct(_M_finish, *(_M_finish - 1)); //placement new一个出来 ++_M_finish; _Tp __x_copy = __x; copy_backward(__position, _M_finish - 2, _M_finish - 1); //增大__position *__position = __x_copy;//拷贝值上去 } else { const size_type __old_size = size(); //获得当前内存量 //这个有个东东要注意,开始插入第一个元素时,size为1;之后都是2倍扩张,oh!vector内存扩展的秘密就在这里 const size_type __len = __old_size != 0 ? 2 * __old_size : 1; iterator __new_start = _M_allocate(__len);//其实就是malloc分配 ,当然内部分配失败会handle的,这里不需要管 iterator __new_finish = __new_start; __STL_TRY {//try的意思 // 下面就是内存从(_M_start, __position)的值复制到(__new_start, __new_start+(__position-_M_start)) // 谁让老家地方不够,现在可好, 要全部转移到高大上的新家了 __new_finish = uninitialized_copy(_M_start, __position, __new_start); // 此时再在新家的下一个可用位置放下新成员__X construct(__new_finish, __x); // 更新以下各可用位置 ++__new_finish; // 这一步怎么来的,因为可能 (__position, _M_finish)之间还有成员,所以一并挪到新家 __new_finish = uninitialized_copy(__position, _M_finish, __new_finish); } // 这里是如果try失败,就回滚,释放分配的内存空间 __STL_UNWIND((destroy(__new_start,__new_finish), _M_deallocate(__new_start,__len))); // 搬进新家,老家就拆了吧,可用继续用作其他开发(比如XXX) destroy(begin(), end()); _M_deallocate(_M_start, _M_end_of_storage - _M_start); // 更新当前的start,finish,storage _M_start = __new_start; _M_finish = __new_finish; _M_end_of_storage = __new_start + __len; } }
3、erase不就是移除元素吗,要知道作为序列式容器 随机移除是O(N)的复杂度,因为要移位,这个原理大家应该都知道,但是被释放的内存哪里去了?
iterator erase(iterator __position) { if (__position + 1 != end())//如果不是最后一个元素 //就把 (__position + 1, _M_finish)上的元素挪到 (__position, (_M_finish-__position + 1))上, // 其实效率更高的是,memmove,STL内部当然很很聪明啦,如果型别是POD就会直接memmove,否则才酱紫挪 copy(__position + 1, _M_finish, __position); // 更新finish位置 --_M_finish; // 释放 _M_finish到哪里呢? 前面其实已经叙述过,如果从内存池分配的就还给内存池;否则才还给OS destroy(_M_finish); return __position; }
好了,自此三个问题回答完毕,有木有很简单。
《STL源码剖析》之vector分析,布布扣,bubuko.com
原文:http://blog.csdn.net/boyxiaolong/article/details/23543663