STL源码分析 # vector #

下面是一个使用vector的demo:

#include <iostream>
#include <vector>

using namespace std;

int main()
{
    /*
    **  To create a vector which contain 10 elements and the value
    **  of each element is 1
    */
    vector<int> vec(10, 1);
    vector<int>::iterator iter = vec.begin();

    for (iter = vec.begin(); iter != vec.end(); iter++)
    {
        cout << *iter << endl;
    }

    return 0;
}

首先要知道class vector是 struct _Vector_base的衍生类.我们看看后者.

又包含一个基类struct _Vector_impl, 这里有三个指针,_M_start, _M_finish, _M_end_of_storage.

默认的初始化为0.

vector采用的数据结构非常简单,就是线性储存空间,它以两个迭代器_M_start, _M_finish分别指向配置得来的连续空间中目前已经使用的范围.并以迭代器_M_end_of_storage指向整块连续空间的尾部(包含备用空间)

_M_start 表示目前使用空间的头

_M_finish 表示目前使用空间的尾

_M_end_of_storage 表示目前可用空间的尾.

至于最后这里的不是很清楚,到现在都没找到如何在一大堆C++代码中快速的定位函数定义的位置.

我们测试一下这种内存构造布局.写个小demo:

/*
Programmer 	: EOF
Date			: 2015.03.31
file				: 4vector-test.cpp

Code description:
	This is a demo to show how the vector works.
*/
#include <iostream>
#include <vector>
#include <algorithm>

using namespace std;

int main()
{
	int i = 0;
	vector<int> iv(2, 9);
	cout << "size" << iv.size() << endl;				// size = 2
	cout << "capacity = " << iv.capacity() << endl; 	// capacity = 2

	iv.push_back(1);
	cout << "size" << iv.size() << endl;				// size = 3
	cout << "capacity = " << iv.capacity() << endl; 	// capacity = 4

	iv.push_back(2);
	cout << "size" << iv.size() << endl;				// size = 4
	cout << "capacity = " << iv.capacity() << endl; 	// capacity = 4

	iv.push_back(3);
	cout << "size" << iv.size() << endl;				// size = 5
	cout << "capacity = " << iv.capacity() << endl; 	// capacity = 8

	iv.push_back(4);
	cout << "size" << iv.size() << endl;				// size = 6
	cout << "capacity = " << iv.capacity() << endl; 	// capacity = 8

	for (i = 0; i < iv.size(); i++)
	{
		cout << iv.at(i) << ' ';
	}
	cout << endl;

	iv.push_back(5);
	cout << "size" << iv.size() << endl;				// size =
	cout << "capacity = " << iv.capacity() << endl; 	// capacity =
	for (i = 0; i < iv.size(); i++)
	{
		cout << iv.at(i) << ' ';
	}
	cout << endl;

	iv.pop_back();
	iv.pop_back();
	cout << "size" << iv.size() << endl;				// size =
	cout << "capacity = " << iv.capacity() << endl; 	// capacity =	

	iv.pop_back();
	cout << "size" << iv.size() << endl;				// size =
	cout << "capacity = " << iv.capacity() << endl; 	// capacity =	

	vector<int>::iterator ivite = find(iv.begin(), iv.end(), 1);

	if (ivite != iv.end())
	{
		iv.erase(ivite);
	}

	cout << "size" << iv.size() << endl;				// size =
	cout << "capacity = " << iv.capacity() << endl; 	// capacity =
	for (i = 0; i < iv.size(); i++)
	{
		cout << iv.at(i) << ' ';
	}
	cout << endl;

	ivite = find(iv.begin(), iv.end(), 2);

	if( ivite != iv.end())
	{
		iv.insert(ivite, 3, 7);
	}

	cout << "size" << iv.size() << endl;				// size =
	cout << "capacity = " << iv.capacity() << endl; 	// capacity =
	for (i = 0; i < iv.size(); i++)
	{
		cout << iv.at(i) << ' ';
	}
	cout << endl;

	iv.clear();
	cout << "size" << iv.size() << endl;				// size =
	cout << "capacity = " << iv.capacity() << endl; 	// capacity =
	return 0;
}

size2

capacity = 2

size3

capacity = 4

size4

capacity = 4

size5

capacity = 8

size6

capacity = 8

9 9 1 2 3 4

size7

capacity = 8

9 9 1 2 3 4 5

size5

capacity = 8

size4

capacity = 8

size3

capacity = 8

9 9 2

size6

capacity = 8

9 9 7 7 7 2

size0

capacity = 8

[Finished in 0.6s]

vector用起来很像C语言的数组,但是这家伙又不是数组,而且比数组快更有效率.

而且还提供下标访问操作.

一下接口供用户访问查看当前vector占用的内存大小

值得注意的是max_size那里,好奇怪为嘛是-1...这里还不是很清楚.

这里应该是一个理论值,vector占满整个内存能装载几个_Tp类型的对象.

begin() end()返回迭代器,其实对于vector来说

我们注意到下标索引实质上是由指针运算实现的.返回引用

  reference operator[](size_type __n) { return *(begin() + __n); }
  const_reference operator[](size_type __n) const { return *(begin() + __n); }

和string一样!!下标索引是不安全的!!用at()函数

  reference at(size_type __n)
    { _M_range_check(__n); return (*this)[__n]; }
  const_reference at(size_type __n) const
    { _M_range_check(__n); return (*this)[__n]; }

vector的构造函数们~ 

  explicit vector(const allocator_type& __a = allocator_type()) //缺省参数,构造一个空的vector
    : _Base(__a) {}

  vector(size_type __n, const _Tp& __value,
         const allocator_type& __a = allocator_type())  //创建n个值为 __value的_Tp类型的vector
    : _Base(__n, __a)
    { _M_finish = uninitialized_fill_n(_M_start, __n, __value); }

  explicit vector(size_type __n)
    : _Base(__n, allocator_type())
    { _M_finish = uninitialized_fill_n(_M_start, __n, _Tp()); }

  vector(const vector<_Tp, _Alloc>& __x) //用别的vector初始化自己这个vector
    : _Base(__x.size(), __x.get_allocator())
    { _M_finish = uninitialized_copy(__x.begin(), __x.end(), _M_start); }

注意看析构函数,这里是调用destroy函数,从_M_start 到 _M_finish之间,而不是_M_start到_M_end_of_storage传递给destroy

  ~vector() { destroy(_M_start, _M_finish); }

我们常常会调用push_back把数据压入到当前vector的最后一个元素之后.

如果内存空间还富余的话(_M_finish 小于 _M_end_of_storage),利用构造函数在_M_finish处构造一个值为__x的vector元素.

如果内存空间不足了,就调用_M_insert_aux在尾部进行插入操作.push_back是没有返回值的!

很多函数都牵扯到_M_insert_aux,而这家伙的实现感觉又找不到,很奇怪...

有一个实现,但是第二个参数是bool在bvector.h里面

front() back()分别返回第一个元素的引用和最后元素的引用.

insert有各种重载,这里贴出一个

在迭代器指向的位置__position插入类型为_Tp的元素__x

erase会删除__first到__last之间的所有元素,而clear()则是调用erase删除所有元素.但是不缩减容量!!

只对_M_start, _M_end进行操作.

时间: 2024-11-05 12:34:12

STL源码分析 # vector #的相关文章

stl源码分析之vector

上篇简单介绍了gcc4.8提供的几种allocator的实现方法和作用,这是所有stl组件的基础,容器必须通过allocator申请分配内存和释放内存,至于底层是直接分配释放内存还是使用内存池等方法就不是组件需要考虑的事情.这篇文章开始分析gcc4.8 stl的容器源码实现.stl的容器分为序列式容器和关联式容器,前者包括vector,list,queue以及stack等常用数据结构,后者包含了map,set以及hash table等比较高级的结构,本文就从使用最广泛也是最基础的vector开始

stl源码分析之hash table

本文主要分析g++ stl中哈希表的实现方法.stl中,除了以红黑树为底层存储结构的map和set,还有用哈希表实现的hash_map和hash_set.map和set的查询时间是对数级的,而hash_map和hash_set更快,可以达到常数级,不过哈希表需要更多内存空间,属于以空间换时间的用法,而且选择一个好的哈希函数也不那么容易. 一. 哈希表基本概念 哈希表,又名散列表,是根据关键字直接访问内存的数据结构.通过哈希函数,将键值映射转换成数组中的位置,就可以在O(1)的时间内访问到数据.举

stl源码分析之list

本文主要分析gcc4.8版本的stl list的源码实现,与vector的线性空间结构不同,list的节点是任意分散的,节点之间通过指针连接,好处是在任何位置插入删除元素都只需要常数时间,缺点是不能随机访问,查询复杂度是O(n),n为list中的元素个数.所以list非常适合应用与数据插入删除频繁的场景. 一. list节点 list节点定义如下, struct _List_node_base { _List_node_base* _M_next; _List_node_base* _M_pre

stl源码分析之priority queue

前面两篇介绍了gcc4.8的vector和list的源码实现,这是stl最常用了两种序列式容器.除了容器之外,stl还提供了一种借助容器实现特殊操作的组件,谓之适配器,比如stack,queue,priority queue等,本文就介绍gcc4.8的priority queue的源码实现. 顾名思义,priority queue是带有优先级的队列,所以元素必须提供<操作符,与vector和list不同,priority queue允许加入元素,但是取出时只能取出优先级最高的元素. 一. pri

STL源码分析--仿函数 &amp; 模板的模板参数 &amp; 临时对象

STL源码分析-使用的一些特殊语法 关于泛型编程中用到的一些特殊语法,这些语法也适用于平常的模板编程 1.  类模板中使用静态成员变量 Static成员变量在类模板中并不是很特殊,同时这个变量不属于对象,属于实例化以后的这个类类型.每一个实例化对应一个static变量 2.  类模板中可以再有模板成员 3.  模板参数可以根据前一个模板参数而设定默认值 4.  类模板可以拥有非类型的模板参数 所谓非类型的模板参数就是内建型的模板参数 Template <class T,class Alloc =

STL源码分析--仿函数 &amp; 配接器

STL源码分析-仿函数 & 配接器 仿函数就是函数对象.就实现观点而言,仿函数其实就是一个"行为类似函数"的对象.为了能够"行为类似函数",其类别定义中必须自定义(或说改写.重载)function call 运算子(operator()),拥有这样的运算子后,我们就可以在仿函数的对象后面加上一对小括号,以此调用仿函数所定义的operator().仿函数作为可配接的关键因素. 配接器在STL组件的灵活组合运用功能上,扮演着轴承.转换器的角色,adapter的定

STL源码之vector

1. SGI的vector SGI stl vector继承子一个基类: template<typename _Tp, typename _Alloc = std::allocator<_Tp> >     class vector : protected _Vector_base<_Tp, _Alloc> 在基类中定义了基本的一些操作,并且封装了了vector所需要的基本的三个指针: struct _Vector_impl       : public _Tp_all

STL源码分析--空间配置器的底层实现 (二)

STL源码分析-空间配置器 空间配置器中门道 在STL中的容器里都是使用统一的空间配置器,空间配置器就是管理分配内存和销毁内存的.在STL将在heap空间创建一个对象分为两个步骤,第一是申请一块内存,第二是在这块内存中初始化一个对象.首先申请空间是由malloc提供,初始化一个对象时由constructor管理.销毁一个对象也是由两步骤完成,第一是销毁空间上的对象,第二是释放这块内存. 同时,STL的空间配置器分为两级内存,如果申请的内存空间大于128KB,那么就使用第一级空间配置,如果小于,那

STL 源码分析 # stl_iterator &amp; stl_iterator_base #

STL 源码分析 # stl_iterator_base && stl_iterator # 这里能很清楚的看到各个基础类型的继承关系 template <class _Tp, class _Distance> struct input_iterator { typedef input_iterator_tag iterator_category; typedef _Tp value_type; typedef _Distance difference_type; typede

STL源码分析--萃取编程(traits)技术的实现

1.为什么要出现? 按照默认认定,一个模板给出了一个单一的定义,可以用于用户可以想到的任何模板参数!但是对于写模板的人而言,这种方式并不灵活,特别是遇到模板参数为指针时,若想实现与类型的参量不一样的实例化,就变得不太可能了!也有时,想禁止此种相同的实例化变得不太可能!故而出现了,Partial Specialization! 同时,在使用void*指针时,可以最大限度的共享代码,减少代码的膨胀! 2.它是什么?其实,就是用户定义的偏特化.用template<>来说明这是一个偏特化,针对任何模板