STL 源码剖析序列式容器之vector(四)

申明:本文主要参考书籍<STL 源码剖析>

1 vector概述

stl vector是序列容器模板类,其支持指定类型的数据存储和随机访问;

对于vector而言,如果在其尾部插入或删除项,其时间复杂度为常量;如果在中间或者头部插入或者删除项,则其时间复杂度为线性的,因为为了保持原本的相对次序,在插入或者删除项之后的所有项都必须进行移动;简单的说:vector类似于动态数组的概念,该动态数据能容纳任何类型的对象,并且可以按需动态伸缩空间;

总结:由于vector维护的是一个连续线性空间,所以vector支持随机存取
      注意:vector动态增加大小时,并不是在原空间之后持续新空间(因为无法保证原空间之后尚有可供配置的空间),而是以原大小的两倍另外配置一块较大的空间,然后将原内容拷贝过来,然后才开始在原内容之后构造新元素,并释放原空间。因此,对vector的任何操作,一旦引起空间重新配置,指向原vector的所有迭代器就都失效了

常用的vector构造函数

  • 构造一个空元素的vector对象

vector<Element>v;

  • 构造一个容纳10个元素的vector对象(并不表示其最多只能容纳10个元素)

vector<Element>v(10);

  • 构造一个容纳10个元素的vector对象并初始化各个元素项

vector<Element>v(10,Element());

  • 构造一个原有的vector对象的拷贝
    vector<Element>v(v1);

添加元素使用push_back函数将元素添加至vector的末尾等其他方式

获得vector的元素个数使用size函数可以获得vector的元素个数,如果判断vector 的元素个数是否为0,建议使用empty函数


2 vector 注意点:

给定一个

vector<int> v;

表达式v[0]生产一个指向vector中第一个元素的引用,所以,&v[0]是指向那个首元素的指针。vector中的元素被C++标准限定为存储在连续内存中,就像是一个数组,所以,如果我们想要传递v给这样的C风格的API:

void doSomething(const int* pInts, size_t numInts);

我们可以这么做:

doSomething(&v[0], v.size());

也许吧。可能吧。唯一的问题就是,如果v是空的。如果这样的话,v.size()是0,而&v[0]试图产生一个指向根本就不存在的东西的指针。这不是件好事。其结果未定义。一个较安全的方法是这样:

if (!v.empty()) {
	doSomething(&v[0], v.size());
}

如果你在一个不好的环境中,你可能会碰到一些半吊子的人物,他们会告诉你说可以用v.begin()代替&v[0],因为(这些讨厌的家伙将会告诉你)begin返回指向vector内部的迭代器,而对于vector,其迭代器实际上是指针。

那经常是正确的,并不总是如此,你不该依赖于此。

begin的返回类型是iterator,而不是一个指针,当你需要一个指向vector内部数据的指针时绝不该使用begin。如果你基于某些原因决定键入v.begin(),就应该键入&*v.begin(),因为这将会产生和&v[0]相同的指针,这样可以让你有更多的打字机会,而且让其他要弄懂你代码得人感觉到更晦涩。坦白地说,如果你正在和告诉你使用v.begin()代替&v[0]的人打交道的话,你该重新考虑一下你的社交圈了。


3 vector 的定义(源代码

#include<iostream>
using namespace std;
#include<memory.h>  

// alloc是SGI STL的空间配置器
template <class T, class Alloc = alloc>
class vector
{
public:
	// vector的嵌套类型定义,typedefs用于提供iterator_traits<I>支持
	typedef T value_type;
	typedef value_type* pointer;
	typedef value_type* iterator;
	typedef value_type& reference;
	typedef size_t size_type;
	typedef ptrdiff_t difference_type;
protected:
	// 这个提供STL标准的allocator接口
	typedef simple_alloc <value_type, Alloc> data_allocator;

	iterator start;               // 表示目前使用空间的头
	iterator finish;              // 表示目前使用空间的尾
	iterator end_of_storage;      // 表示实际分配内存空间的尾

	void insert_aux(iterator position, const T& x);

	// 释放分配的内存空间
	void deallocate()
	{
		// 由于使用的是data_allocator进行内存空间的分配,
		// 所以需要同样使用data_allocator::deallocate()进行释放
		// 如果直接释放, 对于data_allocator内部使用内存池的版本
		// 就会发生错误
		if (start)
			data_allocator::deallocate(start, end_of_storage - start);
	}

	void fill_initialize(size_type n, const T& value)
	{
		start = allocate_and_fill(n, value);
		finish = start + n;                         // 设置当前使用内存空间的结束点
		// 构造阶段, 此实作不多分配内存,
		// 所以要设置内存空间结束点和, 已经使用的内存空间结束点相同
		end_of_storage = finish;
	}

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); }

	// 本实作中默认构造出的vector不分配内存空间
	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)
	{
		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);

	// 提供访问函数
	reference front() { return *begin(); }
	reference back() { return *(end() - 1); }

	
	// 向容器尾追加一个元素, 可能导致内存重新分配
	
	//                          push_back(const T& x)
	//                                   |
	//                                   |---------------- 容量已满?
	//                                   |
	//               ----------------------------
	//           No  |                          |  Yes
	//               |                          |
	//               ↓                          ↓
	//      construct(finish, x);       insert_aux(end(), x);
	//      ++finish;                           |
	//                                          |------ 内存不足, 重新分配
	//                                          |       大小为原来的2倍
	//      new_finish = data_allocator::allocate(len);       <stl_alloc.h>
	//      uninitialized_copy(start, position, new_start);   <stl_uninitialized.h>
	//      construct(new_finish, x);                         <stl_construct.h>
	//      ++new_finish;
	//      uninitialized_copy(position, finish, new_finish); <stl_uninitialized.h>
	

	void push_back(const T& x)
	{
		// 内存满足条件则直接追加元素, 否则需要重新分配内存空间
		if (finish != end_of_storage)
		{
			construct(finish, x);
			++finish;
		}
		else
			insert_aux(end(), x);
	}


	
	// 在指定位置插入元素
	
	//                   insert(iterator position, const T& x)
	//                                   |
	//                                   |------------ 容量是否足够 && 是否是end()?
	//                                   |
	//               -------------------------------------------
	//            No |                                         | Yes
	//               |                                         |
	//               ↓                                         ↓
	//    insert_aux(position, x);                  construct(finish, x);
	//               |                              ++finish;
	//               |-------- 容量是否够用?
	//               |
	//        --------------------------------------------------
	//    Yes |                                                | No
	//        |                                                |
	//        ↓                                                |
	// construct(finish, *(finish - 1));                       |
	// ++finish;                                               |
	// T x_copy = x;                                           |
	// copy_backward(position, finish - 2, finish - 1);        |
	// *position = x_copy;                                     |
	//                                                         ↓
	// data_allocator::allocate(len);                       <stl_alloc.h>
	// uninitialized_copy(start, position, new_start);      <stl_uninitialized.h>
	// construct(new_finish, x);                            <stl_construct.h>
	// ++new_finish;
	// uninitialized_copy(position, finish, new_finish);    <stl_uninitialized.h>
	// destroy(begin(), end());                             <stl_construct.h>
	// deallocate();
	

	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;
	}

	iterator insert(iterator position) { return insert(position, T()); }

	void pop_back()
	{
		--finish;
		destroy(finish);
	}

	iterator erase(iterator position)
	{
		if (position + 1 != end())
			copy(position + 1, finish, position);
		--finish;
		destroy(finish);
		return position;
	}


	iterator erase(iterator first, iterator last)
	{
		iterator i = copy(last, finish, first);
		// 析构掉需要析构的元素
		destroy(i, finish);
		finish = finish - (last - first);
		return first;
	}

	// 调整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()); }

	void clear() { erase(begin(), end()); }

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;
	}

	// 提供插入操作
	
	//                 insert_aux(iterator position, const T& x)
	//                                   |
	//                                   |---------------- 容量是否足够?
	//                                   ↓
	//              -----------------------------------------
	//        Yes   |                                       | No
	//              |                                       |
	//              ↓                                       |
	// 从opsition开始, 整体向后移动一个位置                     |
	// construct(finish, *(finish - 1));                    |
	// ++finish;                                            |
	// T x_copy = x;                                        |
	// copy_backward(position, finish - 2, finish - 1);     |
	// *position = x_copy;                                  |
	//                                                      ↓
	//                            data_allocator::allocate(len);
	//                            uninitialized_copy(start, position, new_start);
	//                            construct(new_finish, x);
	//                            ++new_finish;
	//                            uninitialized_copy(position, finish, new_finish);
	//                            destroy(begin(), end());
	//                            deallocate();
	

	template <class T, class Alloc>
	void insert_aux(iterator position, const T& x)
	{
		if (finish != end_of_storage)    // 还有备用空间
		{
			// 在备用空间起始处构造一个元素,并以vector最后一个元素值为其初值
			construct(finish, *(finish - 1));
			++finish;
			T x_copy = x;
			copy_backward(position, finish - 2, finish - 1);
			*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;
		}
	}

	
	// 在指定位置插入n个元素
	
	//             insert(iterator position, size_type n, const T& x)
	//                                   |
	//                                   |---------------- 插入元素个数是否为0?
	//                                   ↓
	//              -----------------------------------------
	//        No    |                                       | Yes
	//              |                                       |
	//              |                                       ↓
	//              |                                    return;
	//              |----------- 内存是否足够?
	//              |
	//      -------------------------------------------------
	//  Yes |                                               | No
	//      |                                               |
	//      |------ (finish - position) > n?                |
	//      |       分别调整指针                              |
	//      ↓                                               |
	//    ----------------------------                      |
	// No |                          | Yes                  |
	//    |                          |                      |
	//    ↓                          ↓                      |
	// 插入操作, 调整指针           插入操作, 调整指针           |
	//                                                      ↓
	//            data_allocator::allocate(len);
	//            new_finish = uninitialized_copy(start, position, new_start);
	//            new_finish = uninitialized_fill_n(new_finish, n, x);
	//            new_finish = uninitialized_copy(position, finish, new_finish);
	//            destroy(start, finish);
	//            deallocate();
	

	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;
			}
		}
	}
};


4 前面也说了很多访问容器vector的方法(那么也不得不说说迭代器)

标准库还提供了另一种检测元素的方法:使用迭代器(iterator)。迭代器是一种允许程序员检查容器内元素,并实现元素遍历的数据类型

标准库为每一种标准容器(包括vector)定义了一种迭代器类型。迭代器类型提供了比下标操作更一般化的方法:所有的标准库容器都定义了相应的迭代器类型,而只有少数的容器支持下标操作。因为迭代器对所有的容器都适用,现代C++程序更倾向于使用迭代器而不是下标操作访问容器元素,即使对支持下标操作的vector类型也这样。

4.1 vector 的迭代器定义如下:

vector<int>::iterator iter;

 这条语句定义了一个名为iter的变量,它的数据类型是由vector<int>定义的iterator类型。每个标准库容器类型都定义了一个名为iterator的成员,这里的iterator与迭代器实际类型的含义相同。

vector的迭代器具体的使用可以参考<STL 源码剖析>

值得特殊说明:迭代器和迭代器类型

首次遇到有关迭代器的术语时可能会困惑不解,产生困惑的原因之一是由于本书中同一个术语iterator表示两个不同的事物。一般性提及的是迭代器的概念;而特别提及的则是由容器定义的具体的iterator类型,如vector<int>。

  重点要理解的是,定义了许多用作迭代器的类型,这些类型在概念上是相关的。若一种类型支持一组确定的行为(这些行为允许程序员遍历容器内的元素,并允许程序员访问这些元素值),我们就称这种类型为迭代器。

  不同的容器类定义了自己的iterator类型,用于访问容器内的元素。换句话说,每个容器定义了一种名为iterator的类型,而这种类型支持(概念上的)迭代器的各种行为


总结:vector 的迭代器当然还是很复杂,自己可以去深究,我也只是简单的抛出概念。

4.2 vector 的数据结构

     vector数据结构,采用的是连续的线性空间,属于线性存储。他采用3个迭代器_First、_Last、_End来指向分配来的线性空间的不同范围,下面是声明3个迭代器变量的源代码

template<class _Ty, class _A= allocator< _Ty> > 
class vector{ 
    ... 
    protected: 
    iterator _First, _Last, _End; 
};

_First指向使用空间的头部,_Last指向使用空间大小(size)的尾部,_End指向使用空间容量(capacity)的尾部。

借助一个小示例:

int data[6]={3,5,7,9,2,4}; 
vector<int> vdata(data, data+6); 
vdata.push_back(6); 
...

vector初始化时,申请的空间大小为6,存放下了data中的6个元素。当向vdata中插入第7个元素“6”时,vector利用自己的扩充机制重新申请空间,数据存放

结构如下图所示:

当插入第7个元素“6”时,vector发现自己的空间不够了,于是申请新的大小为12的内存空间(自增一倍),并将前面已有数据复制到新空间的前部,然后插入第7个元素。此时_Last迭代器指向最后一个有效元素,而_End迭代器指向vector的最后有效空间位置。我们利用vector的成员函数size可以获得当前vector的大小,此时为7;利用capacity成员函数获取当前vector的容量,此时为12。



5 vector对象的操作

操作调用方式

操作说明

v.empty()

判断v是否为空

v.size()

返回v中元素的个数

v.push_back(t)

v的末尾添加一个元素

V[n]

返回v中位置为n的元素

V1 = v2

v1中元素替换为v2中元素副本

V1==v2

判断是否相等

!=, <, <=, >, >=

直接用于vector对象的相互比较


1.push_back   在数组的最后添加一个数据
2.pop_back    去掉数组的最后一个数据 
3.at                得到编号位置的数据
4.begin           得到数组头的指针
5.end             得到数组的最后一个单元+1的指针
6.front        得到数组头的引用
7.back            得到数组的最后一个单元的引用
8.max_size     得到vector最大可以是多大
9.capacity       当前vector分配的大小
10.size           当前使用数据的大小
11.resize         改变当前使用数据的大小,如果它比当前使用的大,者填充默认值
12.reserve      改变当前vecotr所分配空间的大小
13.erase         删除指针指向的数据项
14.clear          清空当前的vector
15.rbegin        将vector反转后的开始指针返回(其实就是原来的end-1)
16.rend          将vector反转构的结束指针返回(其实就是原来的begin-1)
17.empty        判断vector是否为空
18.swap         与另一个vector交换数据


注,以下是一些需要注意的地方

Ø vectorstring一样,长度、下标等类型是size_type,但是vector获取size_type时,需要指定类型,如vector<int>::size_type这样的方式

Ø vector的下标操作,例如v[i],只能用于操作已经存在的元素,可以进行覆盖、获取等,但是不能通过v[i++]这种方式来给一个vector容器添加元素,该功能需要用push_back操作完成,下标不具备该功能

Ø C++程序员习惯优先使用!=而不是<来编写循环判断条件





评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值