vector的模拟实现(思路清晰+注释详细=包你能看懂+学会)

1. 成员变量

在了解了vector的的底层及基操之后,我们知道,vector是表示可变大小数组的序列容器,而其在底层维护着三个原生态类型的指针(start, finish,end_of_storage),而对vector进行模拟实现,其实说白了就是去使用和维护这三个指针。

namespace my_vector //使用自定义的命名空间,防止跟标准库中的vector起冲突
{
	template<class T> //用模板的方式实现,因为vector可以存放任意类型的元素
	class vector{
	public:
		typedef T* iterator; //迭代器,本质是T*类型的指针
	private:
		iterator start; //指向第一个元素所在的位置
		iterator finish; //指向最后一个元素的下一个位置
		iterator end_of_storage; //指向整个空间的末尾
	};
}

2. 构造和析构(constructor and destructor)

1.1 构造

1.1.1 空的构造

//将指针全部初始化为nullptr即可
vector() 
	: start(nullptr)
	, finish(nullptr)
	, end_of_storage(nullptr)
{}

1.1.2 用n个T类型的元素来构造

注意: 用这个方式进行构造需要注意n的元素类型,如果只给一个size_t类型的 n 的构造方法,形如my_vector::vector v(10, 5); 这样的传参会被当做俩个int类型的参数从而不会调用这个函数,而是去调用下面区间构造的函数导致出错!

解决方案: 提供成对的该拷贝构造函数。

//n的类型为size_t
vector(size_t n, const T &value = T())
	: start(new T[n])
	, finish(start + n)
	, end_of_storage(finish) //这里用finish初始化end_of_storage必须确保finish的声明在后者之前
{ 
    //遍历赋值即可
	for (int i = 0; i < n; i++) {
		start[i] = value;
	}
}

//n的类型为int
vector(int n, const T &value = T())
	: start(new T[n])
	, finish(start + n)
	, end_of_storage(finish) //这里用finish初始化end_of_storage必须确保finish的声明在后者之前
{
	for (int i = 0; i < n; i++) {
		start[i] = value;
	}
}

1.1.3 拷贝构造

//拷贝构造
vector(const vector<T> &v)
{
	start = new T[v.capacity()];
	finish = start + v.size();
	end_of_storage = start + v.capacity();

	for (size_t i = 0; i < v.size(); i++) {
		start[i] = v[i];
	}
}

1.1.4 区间构造

这里需要注意:区间构造所使用的的迭代器应该用模板来重新声明,因为如果使用iterator做迭代器,会导致初始化的迭代器区间[begin,end)只能是vector的迭代器,重新声明迭代器,迭代器区间[begin,end)可以是任意容器的迭代器。

//区间构造,需要使用模板的方式实现
template <class Iterator>
vector(Iterator begin, Iterator end)
{
	int size = end - begin; //区间的元素个数
	start = new T[size];
	finish = start;
	end_of_storage = start + size;

	auto it = begin;
	while (it != end) {
		*finish++ = *it++;
	}
}

1.2 析构

//析构
~vector()
{
	if (start) {
		//释放空间并将指针置为空
		delete[] start;
		start = finish = end_of_storage = nullptr;
	}
}

3. 赋值运算符的重载(operator=)

利用后面实现的swap方法来简单实现赋值运算符的重载, 需要注意的是这里传参必须是以传值的方式进行,如果传递引用会修改右操作数,不符合赋值运算符的本意。

//赋值运算符的重载
vector<T>& operator=(vector<T> v) //注意这里是传值而不是传引用
{
	swap(v); //利用swap将v赋给当前对象
	return *this;
}

4. 迭代器相关(Iterators)

正向迭代器与反向迭代器所在的位置刚好相反。

4.1 正向迭代器

//正向迭代器
iterator begin()
{
	return start;
}

iterator end()
{
	return finish;
}

4.2 反向迭代器

//反向迭代器
iterator rbegin()
{
	return finish;
}

iterator rend()
{
	return start;
}

5. 容量相关(Capacity)

5.1 获取容量相关信息

知识点:指针 - 指针 = 二者之所经历间的元素个数

//有效元素个数
size_t size()const
{
	return finish - start;
}

//容量大小
size_t capacity()const
{
	return end_of_storage - start;
}

//判空
bool empty()const
{
	return start == finish;
}

5.2 更改有效元素个数

//更改有效元素的个数
void resize(size_t newsize, const T &value = T())
{
	size_t oldsize = size();
	if (newsize > size()) { //说明有效元素个数增加
		if (newsize > capacity()) { //需要扩容
			reserve(newsize); //reserve下边实现
		}
		for (size_t i = oldsize; i < newsize; i++) {
			start[i] = value; //填充后续元素
		}
	}

	//不论是有效元素个数增加还是减少,最终都需对finsih进行修改
	finish = start + newsize;
}

5.3 扩容

扩容需要稍微注意的是,不能无脑的直接对齐进行二倍或数倍的扩容,必须考虑刚开始容量为0的这种特殊情况,如果统一以倍数形式增长则这种情况无法扩容成功。

//扩容
void reserve(size_t newcapacity)
{
	size_t oldcapacity = capacity();
	//当新容量大于当前容量时才会真正扩容,否则什么都不做
	if (newcapacity > oldcapacity) {
		//开辟新空间
		T *tmp = new T[newcapacity];
		int n = size(); //记录当前的有效元素个数
		
        //这个判断不能少,否则如果是空的vector,delete nullptr可能会出问题
		if (start) {
			//拷贝元素
			memcpy(tmp, start, sizeof(T)* oldcapacity);
			//释放旧空间
			delete[] start;
		}

		//更新指针
		start = tmp;

		//这里的n不能是size(),因为旧空间已经被释放,此时start已经指向新的空间
		//再使用size()方法去求原来空间的元素个数就可能会有问题
		//这也是需要在一开始记录size()的原因
		finish = start + n;
		end_of_storage = start + newcapacity;
	}
}

6. 元素访问相关(Element access)

这里有一点需要注意:这些方法的实现必须成对给出,分为const类型和非const类型的方法,因为const类型的对象无法调用非const类型的方法。

6.1 下标运算符的重载

//下标运算符的重载,注意需成对实现
T& operator[](size_t pos)
{
	assert(pos < size());
	return start[pos];
}

const T& operator[](size_t pos)const
{
	assert(pos < size());
	return start[pos];
}

6.2 获取头部 & 尾部元素

//头部元素及尾部元素,同样需要成对给出
T& front()
{
	return *start;
}

const T& front()const
{
	return *start;
}

T& back()
{
	return *(finish - 1); //不忘忘记-1!!!
}

const T& back()const
{
	return *(finish - 1);
}

7. 元素修改相关(Modifiers)

7.1 尾部插入 & 删除

//尾部插入
void push_back(const T &value) {
	if (size() == capacity()) { //此时插入元素需要扩容
		if (capacity() == 0) {
			//处理插入元素为第一个元素的情况,如果统一以二倍方式进行扩容则会失败
			reserve(1);
		}
		else {
			reserve(capacity() * 2); //以二倍的方式进行扩容
		}
	}
	*finish++ = value;
}

//尾部删除
void pop_back()
{
	if (empty()) {
		return;
	}
	--finish;
}

7.2 任意位置的插入 & 删除

//任意位置的插入
iterator insert(iterator pos, const T &value)
{
	//扩容处理同上
	if (size() == capacity()) { //此时插入元素需要扩容
		if (capacity() == 0) {
			//处理插入元素为第一个元素的情况,如果统一以二倍方式进行扩容则会失败
			reserve(1);
		}
		else {
			reserve(capacity() * 2); //以二倍的方式进行扩容
		}
	}

	auto it = end();
	while (it != pos) {
		//pos之后的元素需整体向后搬移
		*it = *(it - 1);
		--it;
	}

	//插入元素并更新finish
	*pos = value;
	finish++;
	return pos;
}

//任意位置的删除
iterator erase(iterator pos)
{
	iterator next = pos + 1; //pos的下一个位置
	while (next != finish) {
		*(next - 1) = *next; //数据向前移动
		next++;
	}

	finish--;//更新finish
	return pos;
}

7.3 区间删除 & clear

//区间删除
iterator erase(iterator begin, iterator end)
{
	finish = start;
	return finish;
}

//清空vector
void clear()
{
	//调用erase删除整个区间即可
	erase(begin(), end());
}

7.4 交换操作

最开始的赋值运算符的重载便是调用该swap方法来完成。

//交换操作
void swap(vector<T> &v)
{
	std::swap(start, v.start);
	std::swap(finish, v.finish);
	std::swap(end_of_storage, v.end_of_storage);
}

8. 完整代码

实现在自定义的头文件vecter.h中:

#ifndef _VECTOR_H_ //防止头文件被重复包含
#define _VECTOR_H_

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

namespace my_vector //使用自定义的命名空间,防止跟标准库中的vector起冲突
{
	template<class T> //用模板的方式实现,因为vector可以存放任意类型的元素
	class vector{
	public:
		typedef T* iterator; //迭代器,本质是T*类型的指针
	public:
		//空的构造
		vector() 
			: start(nullptr)
			, finish(nullptr)
			, end_of_storage(nullptr)
		{}

		//用n个T类型的元素来构造
		//n的类型为size_t
		vector(size_t n, const T &value = T())
			: start(new T[n])
			, finish(start + n)
			, end_of_storage(finish) //这里用finish初始化end_of_storage必须确保finish的声明在后者之前
		{
			for (int i = 0; i < n; i++) {
				start[i] = value;
			}
		}

		//n的类型为int
		vector(int n, const T &value = T())
			: start(new T[n])
			, finish(start + n)
			, end_of_storage(finish) //这里用finish初始化end_of_storage必须确保finish的声明在后者之前
		{
			for (int i = 0; i < n; i++) {
				start[i] = value;
			}
		}

		//区间构造,需要使用模板
		template <class Iterator>
		vector(Iterator begin, Iterator end)
		{
			int size = end - begin; //区间的元素个数
			start = new T[size];
			finish = start;
			end_of_storage = start + size;

			auto it = begin;
			while (it != end) {
				*finish++ = *it++;
			}
		}

		//拷贝构造
		vector(const vector<T> &v)
		{
			start = new T[v.capacity()];
			finish = start + v.size();
			end_of_storage = start + v.capacity();

			for (size_t i = 0; i < v.size(); i++) {
				start[i] = v[i];
			}
		}

		//赋值运算符的重载
		vector<T>& operator=(vector<T> v) //注意这里是传值而不是传引用
		{
			swap(v); //利用swap将v赋给当前对象
			return *this;
		}

		
		//迭代器相关

		//正向迭代器
		iterator begin()
		{
			return start;
		}

		iterator end()
		{
			return finish;
		}

		//反向迭代器
		iterator rbegin()
		{
			return finish;
		}

		iterator rend()
		{
			return start;
		}

		
		//容量相关
		//有效元素个数
		size_t size()const
		{
			return finish - start;
		}

		//容量大小
		size_t capacity()const
		{
			return end_of_storage - start;
		}

		//判空
		bool empty()const
		{
			return start == finish;
		}

		//更改有效元素的个数
		void resize(size_t newsize, const T &value = T())
		{
			size_t oldsize = size();
			if (newsize > size()) { //说明有效元素个数增加
				if (newsize > capacity()) { //需要扩容
					reserve(newsize); //reserve后边实现
				}
				for (size_t i = oldsize; i < newsize; i++) {
					start[i] = value; //填充后续元素
				}
			}

			//不论是有效元素个数增加还是减少,最终都需对finsih进行修改
			finish = start + newsize;
		}

		//扩容
		void reserve(size_t newcapacity)
		{
			size_t oldcapacity = capacity();
			//当新容量大于当前容量时才会真正扩容,否则什么都不做
			if (newcapacity > oldcapacity) {
				//开辟新空间
				T *tmp = new T[newcapacity];
				int n = size(); //记录当前的有效元素个数

				//这个判断不能少,否则如果是空的vector,delete nullptr可能会出问题
				if (start) { 
					//拷贝元素
					memcpy(tmp, start, sizeof(T) * oldcapacity);
					//释放旧空间
					delete[] start;
				}

				//更新指针
				start = tmp; 

				//这里的n不能是size(),因为旧空间已经被释放,此时start已经指向新的空间
				//再使用size()方法去求原来空间的元素个数就可能会有问题
				//这也是需要在一开始记录size()的原因
				finish = start + n; 		
				end_of_storage = start + newcapacity;
			}
		}

		
		//元素访问相关
		//下标运算符的重载,注意需成对实现
		T& operator[](size_t pos)
		{
			assert(pos < size());
			return start[pos];
		}

		const T& operator[](size_t pos)const
		{
			assert(pos < size());
			return start[pos];
		}

		//头部元素及尾部元素,同样需要成对给出
		T& front()
		{
			return *start;
		}

		const T& front()const
		{
			return *start;
		}

		T& back()
		{
			return *(finish - 1); //不忘忘记-1!!!
		}

		const T& back()const 
		{
			return *(finish - 1);
		}

		
		//元素修改相关
		//尾部插入
		void push_back(const T &value) {
			if (size() == capacity()) { //此时插入元素需要扩容
				if (capacity() == 0) { 
				//处理插入元素为第一个元素的情况,如果统一以二倍方式进行扩容则会失败
					reserve(1);
				}
				else {
					reserve(capacity() * 2); //以二倍的方式进行扩容
				}
			}
			*finish++ = value;
		}

		//尾部删除
		void pop_back()
		{
			if (empty()) {
				return;
			}
			--finish;
		}

		//任意位置的插入
		iterator insert(iterator pos, const T &value)
		{
			//扩容处理同上
			if (size() == capacity()) { //此时插入元素需要扩容
				if (capacity() == 0) {
					//处理插入元素为第一个元素的情况,如果统一以二倍方式进行扩容则会失败
					reserve(1);
				}
				else {
					reserve(capacity() * 2); //以二倍的方式进行扩容
				}
			}

			auto it = end();
			while (it != pos) { 
				//pos之后的元素需整体向后搬移
				*it = *(it - 1);
				--it;
			}

			//插入元素并更新finish
			*pos = value;
			finish++;
			return pos;
		}

		//任意位置的删除
		iterator erase(iterator pos)
		{
			iterator next = pos + 1; //pos的下一个位置
			while (next != finish) {
				*(next - 1) = *next; //数据向前移动
				next++;
			}

			finish--;//更新finish
			return pos;
		}

		//区间删除
		iterator erase(iterator begin, iterator end)
		{
			finish = start;
			return finish;
		}

		//清空vector
		void clear()
		{
			//调用erase删除整个区间即可
			erase(begin(), end());
		}

		//交换操作
		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()
		{
			if (start) {
				//释放空间并将指针置为空
				delete[] start;
				start = finish = end_of_storage = nullptr;
			}
		}
	private:
		iterator start; //指向第一个元素所在的位置
		iterator finish; //指向最后一个元素的下一个位置
		iterator end_of_storage; //指向整个空间的末尾
	};

}

#endif /* _VECTOR_H_ */

听说帅的人看完都会顺手点赞的哦~

评论 10
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值