C++ STL和泛型编程(二)---- vector

一、结构

在这里插入图片描述

- G2.9下:

容器vector里有三个变量便可操作整个容器,start\finish\end_of_storage:
在这里插入图片描述由图可知,vector里面存放的数据元素只有6个,但实际上容量却有8个元素,且其动态扩展按实际容量的2倍来扩展的即先在其他地方找一个新的内存空间(而不是在原地扩充的),然后再将元素逐一搬过去。

1.变量 start迭代器指向连续空间中已经被使用的空间的开头,即指向存放第一个数据元素的位置;
2.变量 finish迭代器指向已经被使用空间的尾部,即指向存放最后一个数据元素的下一个位置即数据元素以外的紧接着的位置
3.变量 end_of_storage迭代器指向整个连续空间的尾部,即实际已经指向容器以外的位置!!

注意:右图中,finish迭代器所指向的位置是存放最后一个数据元素的下一个数据元素以外的,但此时仍未将容器空间填满,即迭代器finish与迭代器end_of_storage之间还是留有一定空间的。

代码实现:

// G2.9下
template<class T, class Alloc = alloc>
class vector{
public:
	typedef Tvalue_type;
	typedef value_type* iterator; // T*
	typedef value_type& reference;
	typedef size_t size_type;
protected:
	// 所以第一张图中,在G2.9下容器vector的大小为4*3=12(不管放进去的元素)
	// vector实际只有三个指针变量
	iterator start;
	iterator finish;
	iterator end_of_storage;
public:
	iterator begin(){
		return start;
	}
	iterator end(){
		return finish;
	}
	size_type size() const{
		return size_type(end() - begin());
	}
	size_type capacity() const{
		return size_type(end_of_storage - beging());
	}
	bool empty() const{
		return begin() == end();
	}
	reference operator[](size_type n){ // 所以容器若提供连续空间,则一定提供[]的操作:array\vector\deque
		return *(begin() + n);
	}
	reference front(){
		return *begin();
	}
	reference back(){
		return *(end() - 1);
	}
};
  • vector扩展空间的操作:
    在这里插入图片描述
template<class T, class Alloc>
void vector<T, Alloc>::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); // 由前面的allocator分配器知,调用返回的是个指针类型迭代器。
		iterator new_finish = new_start;
		try{
			// 将原来vector的内容拷贝到新vector
			new_finish = uninitialized_copy(start, position, new_start);
			construct(new_finish, x); // 将新元素设初值为x,即在新拷贝好的vector的末端元素的下一个位置new_finish设置初值为新加进来的元素
			++new_finish; // 调整尾迭代器位置
			// 拷贝安插点后的原内容(因为它也可能倍insert(p, x)呼叫)
			new_finish = uninitialized_copy(position, finish, new_finish);
		}
		catch(...){ // 如果加入新元素失败,则回收释放分配的内存空间
			// "commit or rollback" semantics.
			destroy(new_start, new_finish);
			data_allocator::dellocate(new_start, len);
			throw;
		}
		// 解构并释放原vector
		destroy(beging(), end();
		deallocate();
		// 调整迭代器,指向新vector
		start = new_start;
		finish = new_finish;
		end_of_storage = new_start + len; // 指针变量end_of_storage最终的在新vector空间的位置
	}
}

void push_back(const T& x){ // 以引用方式传参(若直接值传递则需调用构造函数创建个新对象,引用传递则不用调用构造函数)
	if(finish != end_of_storage){ // 仍有备用空间
		construct(finish, x);	  // 全局函数  
		++finish;				  // 插入数据元素
	}
	else // 已无备用空间
		insert_aux(end(), x); // 此时的position为end()表示最后元素的下一位的位置,即数据元素以外的紧接着的下一个位置
}

push_back插入新元素的执行过程:
1.首先判断finish是否等于end_of_storage,即原vector内是否还有空间,若有空间则直接调用construct()即在原末端元素的下一个位置插入传入的新元素x。然后通过++finish将finish的位置下移一位。
2.若finish等于end_of_storage,则已无备用空间,所以调用insert_aux(),传入参数为原vector末端元素的下一个位置end()需要添加的新元素x
(1)在insert_aux()内仍需进行一次判断finish是否等于end_of_storage即是否有备用空间(因为insert_aux()除了被push_back()调用外,还可能被insert()调用)。但因为此时是push_back()调用的,且在push_back()中,只有当finish等于end_of_storage才会进入这个环节,因此这时在insert_aux()中,finish是等于end_of_storage的,即无备用空间了。
(2)此时记录下原来的vector实际总元素的大小(包含存放finish和end_of_storage迭代器的元素) old_size,然后定义新分配的总元素的长度 len 以及新分配内存空间的new_start和new_finish迭代器。然后进行try操作放入元素:首先调用uninitialized_copy()函数,传入三个参数,第一个表示所需拷贝内容的起始元素的位置(在push_back()调用下,在原容器中为start的位置),第二个表示当前所需进行插入操作的当前元素位置(在push_back()调用下,position指代的是在原容器中为end()的位置,实际上拷贝内容只选取到position-1位置的元素即end()-1位置的元素),第三个参数则是在新分配的内存空间的起始位置new_start位置开始加入所拷贝的原元素,然后整体uninitialized_copy()函数返回最终新拷贝过去的末端元素的下一个位置,并赋值给new_finish。
(3)接着调用construct()函数,在拷贝后的新的末端元素的下一位置加入新元素x,然后通过++finish将finish的位置下移一位。此时再次调用uninitialized_copy()函数,是考虑insert_aux()函数被insert()调用的情况。而此处是push_back调用insert_aux()函数的,所以此时posiiton是等于finish的,即相当于在new_finish位置加入0个新元素的操作,则操作后new_finish的位置无变化。
(4)catch()是为了新加入元素失败时,回收新分配的内存空间。
(5)将原元素+新元素放入新分配的vector的内存空间后,需要调用destroy()和deallocate()去解构释放原vector容器的内存空间。然后最终更新新vector的三个迭代器信息。

insert插入新元素的执行过程:
1.调用insert_aux(),第一个传入参数表示所需插入元素的当前位置,第二个参数表示当前所需插入的新元素(引用传参避免调用构造函数创建临时变量),然后进行是否有备用空间的判断。
2.若finish != end_of_storage即仍有备用空间,则因为此时是在某个位置进行插入操作,所以调用construct()时,是在原finish的位置,放入原vector的最后一个元素即*(finish-1)的值。然后++finish将finish下移一位。接着定义x_copy并赋值为新加入的元素。
调用逆序拷贝的copy_back(first, last, result)函数
【注意拷贝内容范围是[first, last)即last不是拷贝内容,last-1才是起始拷贝内容,而操作目标的起始位置是result - 1
(1)第一个参数为拷贝内容的起始位置即为当前需要插入的位置position;
(2)第二个参数为拷贝内容的结束位置即finish-2(前两个表示的是拷贝源即拷贝内容的来源):注意起始拷贝的内容并不是finish-2而是finish-3,因为是[first, last),前闭后开区间;
(3)第三个参数是所进行操作的拷贝目标的拷贝起始位置即finish-1(表示需要进行操作的拷贝目标):注意是逆序从新扩充的空间finish-1处再往前一个位置开始实际上开始操作的是finish-2处的内存
最后逆序拷贝完后,再将当前值更新为新插入的元素值。
3.若finish = end_of_storage即无备用空间时,则在分配新的空间时,大部分过程与push_back的流程类似。而稍微有不同的是,在进入到try{}中的第二个uninitialized_copy()函数,此时,position != finish,所以此时在前半段的拷贝结束元素的下一个位置即new_finish位置开始插入后半端的内容即原容器中[position, finish)的拷贝内容。余下过程均类似。

链接:
copy_back()的实现
在这里插入图片描述

- G4.9下:

在这里插入图片描述
结构复杂化了,有继承关系、复合关系。要知道vector的大小,从图可知:
vector<_Tp>里面没有变量,往上看其继承的父类_Vector_base<_Tp>中有一个变量_M_impl,且其为_Vector_impl类;
而再由复合关系知,_Vector_impl类有三个变量_M_start\ _M_finish\ _M_end_of_storage,为指针变量;
再看其上继承的allocator类里面不含变量,所以在G4.9下,vector的大小为4*3=12。

二、迭代器

- G2.9下:

在这里插入图片描述由上知,iterator为native pointer类型,非class iterator,
所以通过萃取机,选择偏特化的版本,来回答algorithm提出的三个问题:iterator_category\ difference_type\ value_type的类型问题(分别为 random_access_iterator_tag\ ptrdiff_t\ T)。

- G4.9下:

在这里插入图片描述由图,iterator是__gnu_cxx::__normal_iterator<pointer, vector>类的类对象,而该类里面有_Iterator型变量_M_current,而_Iterator为传入模板参数,推知其为_Base::pointer型,而_Base又为_Vector_base<Tp, _Alloc>类,则_Base::pointer为_Vector_base<Tp, _Alloc>类的类对象_Base中的pointer类型,而此pointer又为__gnu_cxx::_alloc_traits<_Tp_alloc_type>::pointer类,再往前找可知其为allocator类中的Tp*类(别名为pointer)。最终知道iterator为Tp*类。

在这里插入图片描述由上知,此时G4.9下的vector容器的iterator为class iterator类型,所以不再是native pointer类型,所以萃取机将其分成第二类操作,即该iterator可支持5种associated types的提问。

链接:
vector剖析

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值