【STL简单源码剖析】vector的实现

收灯庭院迟迟月

落索秋千翦翦风


目录

 vector 简单概述

vector 代码摘要

vector 的迭代器

vector 成员变量

vector 构造析构

获取当前元素个数  

获取当前容器容量 

operator[]的运算符重载

vector 空间预留

push_back() 的实现

迭代器失效的简单讲解 

pop_back() 的实现

 insert() 的实现

 erase() 的实现

vector 拷贝构造

vector 赋值构造

vector 迭代器构造

vector 字符构造 

vector 链式构造 

契子


 vector 简单概述

vector 的資料安排以及操作方式,与 array 非常像似。兩者的唯一差別在於空間的運用彈性


array 是靜態空間,一旦配置了就不能改變;要換個大(或小) 一點的房子,可以,一切細瑣得由客端自己來:首先配置一塊新空間,然後將元素從舊址一一搬往新址,然後再把原來的空間釋還給系統


vector 是動態空間, 隨著元素的加入,它的內部機制會自行擴充空間以容納新元素。因此,vector 的運用對於記憶體的樽節與運用彈性有很大的幫助,我們再也不必因為害怕空間不足而一開始就要求一個大塊頭 array 了,我們可以安心使用 vector,吃多少用多少

我们不仅要会用 vector 的接口使用,还需要了解底层的代码逻辑,学习更新优秀的代码思维 ~

本篇文件将会带大家学习 vector 的简单实现,提前说明一下并不会涉及内存池的相关操作 ~


vector 代码摘要

	template<class T>
	class vector
	{
	public:
		typedef T* iterator;
		typedef const T* const_iterator;

		//迭代器
		iterator begin()
		{
			return _start;
		}

		iterator end()
		{
			return _finish;
		}

		const_iterator begin() const
		{
			return _start;
		}

		const_iterator end() const
		{
			return _finish;
		}

		//构造
		vector()
			:_start(nullptr), _finish(nullptr), _end_of_storage(nullptr)
		{}
		//析构
		~vector()
		{
			if (_start)
			{
				delete[] _start;
				_start = _finish = _end_of_storage;
			}
		}

		size_t size() const
		{
			return _finish - _start;
		}

		size_t capacity() const
		{
			return _end_of_storage - _start;
		}

	private:
		iterator _start;
		iterator _finish;
		iterator _end_of_storage;
	};

以上是 vector 的雏形

vector 的迭代器

vector 的底层是一个连续性的空间,所以不管其元素类型如何

其原生指针都可以作为 vector 的迭代器而满足所有条件,因为 vector 迭代器所需要的操作:

operator* operator-> operator++ operator-- operator+ operator- operator+= operator-=

原生指针天生就具备,所以 vector 支持随时存取,而原生指针正有着这样的能力

简单来说就像我们之前实现 string 库一样,我们可以使用模板来实现原生指针

typedef T* iterator;
typedef const T* const_iterator;

就是以上的代码,不管是怎样的类型我们的迭代器都可以使用 ~

vector 成员变量

vector 采用的结构非常简单:线性且连续的空间,它以两个迭代器 start 和 finish 分别指向配置得来的连续空间中目前已被使用的范围(size),并以迭代器 end_of_storage 指向连续空间的末尾(capacity

	template<class T>
	class vector
	{
	public:
		typedef T* iterator;
		typedef const T* const_iterator;
    // ... 
	private:
		iterator _start;            //表示目前使用空间的头
		iterator _finish;           //表示目前使用空间的尾
		iterator _end_of_storage;   //表示目前可用空间的尾
	};

为了降低空间配置时的速度成本,vector 实际配置的大小可能比客端需求量更大一些,以备将来可能的扩充。这便是容量(capacity)的概念。换一句话说一个 vector 的容量永远大于或等于其大小。一旦容量等于大小,便是满载,下次再有新增元素,整个 vector 就得另觅居所。 使用 start finish end_of_storage 三个迭代器,便可轻易提供首尾位置、 大小、容量、空容器判断、运算符[ ] 的使用、最前端元素值、最后端元素值… 等功能

 

vector 构造析构

构造


		vector()
			:_start(nullptr), _finish(nullptr), _end_of_storage(nullptr)
		{}

析构

        
        ~vector()
		{
			if (_start)
			{
				delete[] _start;
				_start = _finish = _end_of_storage;
			}
		}

 

获取当前元素个数  

<1>这里迭代器指向的空间的地址,所以我们用 元素末尾的迭代器 - 元素起始的迭代器 会得到相差的字节数,我们将其转化为整型就是当前元素个数了

<2> size() 成员函数加个 const,这样 const 和非 const 对象都就可以调用了

		size_t size() const
		{
			return _finish - _start;
		}

 

获取当前容器容量 

 

		size_t capacity() const
		{
			return _end_of_storage - _start;
		}

operator[]的运算符重载

		T& operator[](size_t pos) 
		{
			assert(pos < size());
			return  *(_start + pos);
		}

vector 空间预留

void reserve(size_t n)
{
	if (n > capacity())
	{
		T* tmp = new T[n];
		size_t lod_size = size();
		if (_start)
		{
			for (int i = 0; i < size(); i++)
			{
				tmp[i] = _start[i];
			}
			delete[] _start;
		}
		_start = tmp;
		_finish = _start + lod_size;
		_end_of_storage = _start + n;
	}
}

以上我们为什么要用一个 lod_size 呢?
我们知道扩容操作的本质并不是从末尾追加空间,而是找一块合适的空间并将原内容拷贝过去,再释放原空间

不光是内容要变,我们迭代器的指向也要变,而我们设置  lod_size 正是为了确立 _finish 的指向

表明 _finish 在原空间的第几号位置,在新空间同样如此

push_back() 的实现

push_back() 就是我们常见的尾插接口,当我们以 push_back() 将新元素插入到 vector 尾端,应该先检查是否还有足够的空间?如果有就直接在足够空间上建立元素,并调整迭代器_finish ,使 vector 变大。如果没有足够空间了,就扩充空间

(重新配置、搬移资料、释放原空间)

void push_back(const T& x)
{
	if (size() == capacity())
	{
		size_t newcapacity = capacity() == 0 ? 4 : 2 * capacity();
		reserve(newcapacity);
	}
	*_finish = x;
	++_finish;
}

 我们来浅浅测试一下:

void Test_vector()
{
		vector<int> str;
		str.push_back(1);
		str.push_back(2);
		str.push_back(3);
		str.push_back(4);
		str.push_back(5);
		for (int i = 0; i < str.size(); i++)
		{
			cout << str[i] << " ";
		}
		cout << endl;
}

 

迭代器失效的简单讲解 

注意:

对 vector 的任何操作,一旦引起空间重新配置,指向原 vector 的所有迭代器就都失效了!!!

void Test_vector()
{
	vector<int> str;
	str.push_back(1);
	str.push_back(2);
	str.push_back(3);
	str.push_back(4);
	auto it = str.begin();
	str.push_back(5);
	while (it != str.end())
	{
		cout << *it;
		it++;
	}
}

大家看一下以上代码是否还会输出正确的结果:

我们发现我们用迭代器打印出来的这样一团糟的结果

但是呢 -- 如果我们将迭代的输出控制在扩容之前(我们发现就是正确的结果)

原因:it 指向初始空间的首元素,尾插元素时空间不足,开辟更大的新空间存储元素,释放旧空间,但此时 it 仍旧指向已经被释放的旧空间,成为了野指针。在循环中试图对野指针进行解引用导致程序崩溃,所以扩容会导致迭代器的失效

那么这一类问题要怎么解决呢? --  在所有可能导致迭代器失效的操作之后,如果要使用迭代器,在使用前重新赋值,使其指向新空间

oid Test_vector()
{
	vector<int> str;
	str.push_back(1);
	str.push_back(2);
	str .push_back(3);
	str.push_back(4);
	auto it = str.begin();
	str.push_back(5);
    //对迭代器重新赋值
	it = str.begin();
	while (it != str.end())
	{
		cout << *it;
		it++;
	}
}

不光是增加元素,减少元素的操作(比如说:erase)可能也会导致迭代器失效,因为你不知道在哪个平台下,编译器是否会缩容

所以我们在访问迭代器的时候需要特别谨慎 !!!一定要先更新再使用

pop_back() 的实现

pop_back() 很简单,不过要先判断一下要删除的位置是否合法哦 ~ 发生越界就惨了

		void pop_back()
		{
			assert(_start < _finish);
			--_finish;
		}
//代码测试
void Test_vector()
{
	vector<int> str;
	str.push_back(1);
	str.push_back(2);
	str .push_back(3);
	str.push_back(4);
	str.push_back(5);
	for (auto i : str)
	{
		cout << i << " ";
	}
	cout << endl;
	str.pop_back();
	str.pop_back();
	for (auto i : str)
	{
		cout << i << " ";
	}
	cout << endl;
}

 insert() 的实现

insert() 的作用就是在我们的指定位置插入一个元素。我们这里采用支持传迭代器的形式,为了更新我们的迭代器我们要返回更新后的 pos 值(因为扩容后 pos 的位置也会发生改变)。采用 log_size 是为了确认我们的 pos 在原空间的位置,然后在新空间进行精准定位。然后就是挪动数据与插入数据的常规操作了 ~

iterator insert(iterator pos, const T& x)
{
	assert(_start <= pos && pos <= _finish);
	size_t lod_size = pos - _start;
	if (size() == capacity())
	{
		size_t newcapacity = capacity() == 0 ? 4 : 2 * capacity();
		reserve(newcapacity);
		pos = _start + lod_size;
	}
	iterator end = _finish - 1;
	while (end >= pos)
	{
		*(end + 1) = *end;
		--end;
	}
	*pos = x;
	++_finish;
	return pos;
}
//代码测试
void Test_vector()
{
	vector<int> str;
	str.push_back(1);
	str.push_back(2);
	str .push_back(3);
	str.push_back(4);
	str.push_back(5);
	for (auto i : str)
	{
		cout << i << " ";
	}
	cout << endl;
	str.insert(str.begin(), 100);
	for (auto i : str)
	{
		cout << i << " ";
	}
	cout << endl;
}

 

 erase() 的实现

erase() 就是删除指定位置的元素,不过我们想先判断一下 pos 的合法范围,在挪动元素进行覆盖

		void erase(iterator pos)
		{
			assert(_start <= pos && pos < _finish);
			iterator end = pos + 1;
			while (end <= _finish)
			{
				*(end - 1) = *end;
				++end;
			}
			--_finish;
		}
//代码测试
void Test_vector()
{
	vector<int> str;
	str.push_back(1);
	str.push_back(2);
	str .push_back(3);
	str.push_back(4);
	str.push_back(5);
	for (auto i : str)
	{
		cout << i << " ";
	}
	cout << endl;
	str.erase(str.begin());
	for (auto i : str)
	{
		cout << i << " ";
	}
	cout << endl;
}

vector 拷贝构造

		vector(const vector<T>&v)
		{
			_start = new T[v.capacity()];
			for (size_t i = 0; i < v.size(); i++)
			{
				_start[i] = v._start[i];
			}
			_finish = _start + v.size();
			_end_of_storage = _start + v.capacity();
		}
//代码测试
void Test_vector()
{
	vector<int> str;
	str.push_back(1);
	str.push_back(2);
	str .push_back(3);
	str.push_back(4);
	str.push_back(5);
	for (auto i : str)
	{
		cout << i << " ";
	}
	cout << endl;
	vector<int> arr(str);
	for (auto i : arr)
	{
		cout << i << " ";
	}
	cout << endl;
}

 

vector 赋值构造

我们可以先写一个类部的交换函数 ~

		void swap(vector<T>& v)
		{
			std::swap(_start, v._start);
			std::swap(_finish, v._finish);
			std::swap(_end_of_storage, v._end_of_storage);
		}

然后直接复用:这里我们是传值调用,调用(深)拷贝函数生成一个新空间装 v 的内容,然后我们通过 swap 将这块空间拿过来用,最后返回 *this 即可 ~ 是不是很方便

		vector<T>& operator=(vector<T> v) 
		{
			swap(v);
			return *this;
		}
//代码测试
void Test_vector()
{
	vector<int> str;
	str.push_back(1);
	str.push_back(2);
	str .push_back(3);
	str.push_back(4);
	str.push_back(5);
	for (auto i : str)
	{
		cout << i << " ";
	}
	cout << endl;
	vector<int> arr;
	arr = str;
	for (auto i : arr)
	{
		cout << i << " ";
	}
	cout << endl;
}

vector 迭代器构造

我们这里传的是类模板,也就是说我们可以传迭代器哦 ~  

		template <class InputIterator>
		vector(InputIterator first, InputIterator last)
		{
			while (first != last)
			{
				push_back(*first);
				++first;
			}
		}
//代码测试
void Test_vector()
{
	vector<int> str;
	str.push_back(1);
	str.push_back(2);
	str .push_back(3);
	str.push_back(4);
	str.push_back(5);
	for (auto i : str)
	{
		cout << i << " ";
	}
	cout << endl;
	vector<int> arr(str.begin()+1,str.end());
	for (auto i : arr)
	{
		cout << i << " ";
	}
	cout << endl;
}

vector 字符构造 

这个函数的意思是:采用 n 个 val 值进行初始化

而使用 T& val = T() 的原因为我们想要无参的效果

原因是 T& val = ?我们发现这个参数写什么类型都不好,所以我们采用了匿名对象 T() 

		vector(size_t n, const T& val = T())
		{
			reserve(n);
			for (size_t i = 0; i < n; i++)
			{
				push_back(val);
			}
		}
//代码测试
void Test_vector()
{
	vector<string> arr(10, "xxx");
	for (auto i : arr)
	{
		cout << i << " ";
	}
	cout << endl;
}

注意:

void Test_vector()
{
	vector<int> arr(10, 0);
	for (auto i : arr)
	{
		cout << i << " ";
	}
	cout << endl;
}

如果我们这么写,就会报一个这样的错误!!!这是为什么呢

原因是编译器会调用更匹配的函数 -- 调到 迭代器构造 了

你看我们的函数参数类型是 size_t 和 int(传化后的)

vector(size_t n, const T& val = T())

而我们传参类型为 int 和 int 

vector<int> arr(10, 0);

我们要使用这个函数左参数还需要强制类型转化,而我们的模板任何类型都能用,所以它调到迭代器构造才发生了这样的错误

所以我们只需再写一个专门争对这一个情况的函数即可:

		vector(int n, const T& val = T())
		{
			reserve(n);
			for (int i = 0; i < n; i++)
			{
				push_back(val);
			}
		}

 

vector 链式构造 

想必大家都用过这种形式的初始化,而这里主要采用了我们链表的功能 ,所以我们称链式构造

		vector(initializer_list<T> il)
		{
			reserve(il.size());
			for (auto i:il)
			{
				push_back(i);
			}
		}

 感觉很简单只需要把 { } 内容一次尾插到我们的容器中即可

//代码测试
void Test_vector()
{
	vector<int> arr = { 1,2,3,4,5,6 };
	for (auto i : arr)
	{
		cout << i << " ";
	}
	cout << endl;
}

整体代码的实现

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

namespace Mack
{
	template<class T>
	class vector
	{
	public:
		typedef T* iterator;
		typedef const T* const_iterator;

		vector(initializer_list<T> il)
		{
			reserve(il.size());
			for (auto i:il)
			{
				push_back(i);
			}
		}

		//迭代器
		iterator begin()
		{
			return _start;
		}

		iterator end()
		{
			return _finish;
		}

		const_iterator begin() const
		{
			return _start;
		}

		const_iterator end() const
		{
			return _finish;
		}

		template<class InputIterator>
		vector(InputIterator first, InputIterator last)
		{
			while (first != last)
			{
				push_back(*first);
				first++;
			}
		}


		//拷贝构造
		vector(const vector<T>&v)
		{
			_start = new T[v.capacity()];
			for (size_t i = 0; i < v.size(); i++)
			{
				_start[i] = v._start[i];
			}
			_finish = _start + v.size();
			_end_of_storage = _start + v.capacity();
		}

		void swap(vector<T>& v)
		{
			std::swap(_start, v._start);
			std::swap(_finish, v._finish);
			std::swap(_end_of_storage, v._end_of_storage);
		}

		//赋值构造
		vector<T>& operator=(vector<T> v) 
		{
			swap(v);
			return *this;
		}

		//n个val值初始化
		vector(size_t n, const T& val = T())
		{
			reserve(n);
			for (size_t i = 0; i < n; i++)
			{
				push_back(val);
			}
		}

		vector(int n, const T& val = T())
		{
			reserve(n);
			for (int i = 0; i < n; i++)
			{
				push_back(val);
			}
		}

		//构造
		vector()
			:_start(nullptr), _finish(nullptr), _end_of_storage(nullptr)
		{}
		//析构
		~vector()
		{
			if (_start)
			{
				delete[] _start;
				_start = _finish = _end_of_storage;
			}
		}

		size_t size() const
		{
			return _finish - _start;
		}

		size_t capacity() const
		{
			return _end_of_storage - _start;
		}

		T& operator[](size_t pos) 
		{
			assert(pos < size());
			return  *(_start + pos);
		}

		void reserve(size_t n)
		{
			if (n > capacity())
			{
				T* tmp = new T[n];
				size_t lod_size = size();
				if (_start)
				{
					for (int i = 0; i < size(); i++)
					{
						tmp[i] = _start[i];
					}
					delete[] _start;
				}
				_start = tmp;
				_finish = _start + lod_size;
				_end_of_storage = _start + n;
			}
		}

		void push_back(const T& x)
		{
			if (size() == capacity())
			{
				size_t newcapacity = capacity() == 0 ? 4 : 2 * capacity();
				reserve(newcapacity);
			}
			*_finish = x;
			++_finish;
		}

		void pop_back()
		{
			assert(_start < _finish);
			--_finish;
		}

		iterator insert(iterator pos, const T& x)
		{
			assert(_start <= pos && pos <= _finish);
			size_t lod_size = pos - _start;
			if (size() == capacity())
			{
				size_t newcapacity = capacity() == 0 ? 4 : 2 * capacity();
				reserve(newcapacity);
				pos = _start + lod_size;
			}
			iterator end = _finish - 1;
			while (end >= pos)
			{
				*(end + 1) = *end;
				--end;
			}
			*pos = x;
			++_finish;
			return pos;
		}

		void erase(iterator pos)
		{
			assert(_start <= pos && pos < _finish);
			iterator end = pos + 1;
			while (end <= _finish)
			{
				*(end - 1) = *end;
				++end;
			}
			--_finish;
		}

	private:
		iterator _start;
		iterator _finish;
		iterator _end_of_storage;
	};

}

先介绍到这里啦~

有不对的地方请指出💞

评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

烟雨长虹,孤鹜齐飞

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值