(超详细 + 逐个解读) C++的STL学习 6 : vector的模拟实现 (具体实现)

上一次我们认识了vector的各种接口, 我们可以发现, STL容器中的许多接口都是非常类似的, 使用方法也很相似, 方便了我们的使用. 那么今天, 我们趁热打铁, 来模拟实现一下vector, 加深对vector容器的理解. 耐心看完, 你一定有所收获~

一. vector的结构描述

vector实际上是由三个指针描述的, 如下如所示 :

在这里插入图片描述

所以vector的成员就是三个指针

而且vector应用模板, 实现泛型

template <class T>
class Vector {

private:
	T* _start;		//start: 首元素地址
	T* _finish;		//finish: 最后一个元素的下一个位置
	T* _endofstorage;	//endofstorage: 整个空间的尾地址
}

二. 基本功能

1. 构造函数

	//构造函数
	Vector()
		: _start(nullptr)
		, _finish(nullptr)
		, _endofstorage(nullptr)
	{}

这里给出的是一个无参的默认构造, 给三个指针都赋值为nullptr

这里也可以实现其他的构造函数, 比如用n个数据val构造, 开好空间之后进行循环尾插即可

2. 拷贝构造

	//拷贝构造
	Vector(const Vector& v)
		: _start(nullptr)
		, _finish(nullptr)
		, _endofstorage(nullptr)
	{
		//开好空间
		reserve(v.capacity());
		iterator it = begin();
		const_iterator vit = v.begin();
		//遍历赋值
		while (vit != v.end()) {
			*it = *vit;
			it++;
			vit++;
		}
		//修改_finish指针
		_finish += v.size();
	}

思路:
1. 先开好空间, 避免之后多次新开空间
2. 拿到当前对象*this 和 要赋值对象v 的迭代器
3. 循环赋值(深拷贝)
4. 修改_finish指针 (实际上就是修改有效元素个数 size)

3. 赋值运算符

	//交换函数
	void Swap(Vector<T> v) {
		swap(_start, v._start);
		swap(_finish, v._finish);
		swap(_endofstorage, v._endofstorage);
	}

	//赋值运算符
	Vector<T>& operator=(Vector<T> v) {
		Swap(v);
		return *this;
	}

赋值运算符的现代写法
自定义一个交换函数, 把成员全部交换给创建好的临时变量即可

4. 析构函数

	//析构
	~Vector() {
		if (_start) {
			delete[] _start;
			_start = _finish = _endofstorage = nullptr;
		}
	}

析构函数: 释放资源, 也就是把这个数组的空间释放即可

三. 迭代器

首先来看迭代器的声明:

	//Vector迭代器:  T*
	typedef T* iterator;
	typedef const T* const_iterator;

1. begin 和 end

	iterator begin() {
		return _start;
	}

	iterator end() {
		return _finish;
	}

	const_iterator begin() const {
		return _start;
	}

	const_iterator end() const {
		return _finish;
	}

2. operator[ ]

	//operator[]   获取pos位置元素
	T& operator[](size_t pos) {
		assert(pos < size());
		return _start[pos];
	}
	
	// [] const接口
	const T& operator[](size_t pos) const {
		assert(pos < size());
		return _start[pos];
	}

四. 容量相关操作

1. size

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

在刚开始的时候那个图还记得吗?... _finish - _start 即可

2. capacity

	//获取容量capacity
	size_t capacity() const {
		return _endofstorage - _start;
	}

还是那副图, 指针相减即可

3. reserve增容 (memcpy拷贝的问题 *)

	//增容
	void reserve(size_t n) {
		if (n > capacity()) {
			//保存size
			size_t sz = size();

			//1. 开空间
			T* tmp = new T[n];
			
			//2. 内容拷贝

			//memcpy为内存拷贝, 是浅拷贝, 遇到有资源的类型会发生问题
			//memcpy(tmp, _start, sizeof(T) * size());

			//执行T类型的赋值操作, 进行深拷贝
			for (int i = 0; i < sz; ++i) {
				tmp[i] = _start[i];
			}

			//3. 释放原有空间
			delete[] _start;

			//4. 更新成员变量
			_start = tmp;
			_endofstorage = _start + n;
			_finish = _start + sz;
		}
	}

相信看过之前实现的应该明白这都是老步骤了
开空间, 拷贝内容, 释放原有空间, 更新指针指向 和 size capacity
最后更新capacity的时候: _start + n即可
而更新size 的时候: _start + 有效元素的个数, 这里不知道, 所以要在开头保存一下原有的size

接下来说一下memcpy的拷贝问题

我们知道, memcpy是字节拷贝(内存拷贝), 而根据经验, 字节拷贝都是浅拷贝, 在涉及资源时是行不通的. 下面我们就来看看是怎么回事

这里不好直接描述, 下面直接给出图示:

在这里插入图片描述

4. resize

	//设置size
	void resize(size_t n, const T& val = T()) {
		//增容
		if (n > capacity()) {
			reserve(n);
		}

		//给新的位置赋值 [size, n)
		if (n > size()) {
			iterator it = end();
			while (it != _start + n) {
				*it = val;
				++it;
			}
		}

		//修改size
		_finish = _start + n;
	}

resize(n, ch)
1. n <= size: 只需要修改size
2. size < n <= capacity: 赋值ch --> [size, capacity) , 修改size
3. n > capacity: 增容, 赋值, 修改size

五. 插入和删除

1. insert

	//插入  (在pos位置前插入一个元素)
	void insert(iterator pos, const T& val) {
		assert(pos >= begin() && pos <= end());

		//获取pos 和 begin的偏移量
		int offset = pos - _start;

		//检查容量
		if (_finish == _endofstorage) {
			size_t newCap = (_start == nullptr) ? 1 : 2 * capacity();
			reserve(newCap);
		}
		
		//增容后, 原有空间被释放, pos迭代器失效, 需要更新pos的位置
		pos = _start + offset;

		//元素移动 --- 从后向前
		iterator it = end();
		while (it != pos) {
			*it = *(it - 1);
			--it;
		}

		//插入
		*pos = val;

		//更新size
		++_finish;
	}

还是熟悉的步骤:

  1. 检查容量, 是否需要增容
  2. 元素移动 — 从后往前
  3. 插入, 更新size
    vector中要注意增容后迭代器失效的问题, 所以开始要获取与start的偏移量

2. erase

	//删除   (删除pos位置的元素)
	iterator erase(iterator pos) {
		assert(pos >= begin() && pos < end());

		//移动元素 --- 从前向后
		iterator it = pos + 1;
		while (it != end()) {
			*(it - 1) = *it;
			//*it = *(it + 1);  错误的, 因为end()位置不是有效位置, 没有元素
			++it;
		}

		//更新size
		--_finish;

		//返回被删除元素的下一个位置, 即pos的位置 (被删除的下一个位置移到pos这了)
		return pos;
	}
  1. 移动元素 ---- 从前向后
  2. 更新size

这里不再实现头插, 尾插, 头删, 尾删等等接口, 因为可以直接给出位置复用以上两个代码即可

六. 测试

构造, 拷贝, = , 3种遍历的测试

void test() {
	Vector<int> v;
	v.push_back(1);
	v.push_back(2);
	v.push_back(3);
	v.push_back(4);
	v.push_back(5);
	v.push_back(6);


	Vector<int> v2(v);
	v2.pop_back();
	Vector<int> v3;
	v3 = v2;
	v3.pop_back();

	//[]遍历
	cout << "operator[]: " << endl;
	for (size_t i = 0; i < v.size(); i++) {
		cout << v[i] << " ";
		//可以修改
		v[i] = 10;
	}
	cout << endl;

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

	//迭代器
	cout << "iterator: " << endl;
	Vector<int>::iterator it = v.begin();
	while (it != v.end()) {
		cout << *it << " ";
		*it = 5;
		it++;
	}
	cout << endl;

	it = v.begin();
	while (it != v.end()) {
		cout << *it << " ";
		it++;
	}
	cout << endl;

	//范围for
	cout << "范围for: " << endl;
	for (auto& e : v) {
		cout << e << " ";
		e = 7;
	}
	cout << endl;

	for (const auto& e : v) {
		cout << e << " ";
	}
	cout << endl;
}

运行结果如下:

在这里插入图片描述

插入, 删除 , resize的测试

template <class T>
void printV(const Vector<T>& v) {
	for (const auto& e : v) {
		cout << e << " ";
	}
	cout << endl;
}

void test2() {
	Vector<int> v;
	v.push_back(1);
	v.push_back(2);
	v.push_back(3);
	v.push_back(4);
	v.push_back(5);
	v.push_back(6);

	printV(v);
	v.resize(9);
	printV(v);
	v.resize(4, 20);
	printV(v);
	v.resize(6, 30);
	printV(v);
}

运行结果如下:

在这里插入图片描述

void test3() {
	Vector<int> v;
	v.push_back(1);
	v.push_back(2);
	v.push_back(3);
	
	v.insert(v.end(), 4);
	v.insert(v.begin(), 0);

	printV(v);
}

运行结果如下 :
在这里插入图片描述

删除重复的元素

void test4() {
	Vector<int> v;
	v.push_back(1);
	v.push_back(1);
	v.push_back(2);
	v.push_back(3);
	v.push_back(1);
	v.push_back(1);
	v.push_back(1);
	printV(v);

	//删除所有的1
	Vector<int>::iterator it = v.begin();
	while (it != v.end()) {
		//删除之后, 要从新获取erase接口的返回值, 让it更新到被删除元素的下一个位置
		if (*it == 1)
			it = v.erase(it);
		else
			++it;
	}
	printV(v);
}

运行结果如下 :
在这里插入图片描述

vector中套string类型测试

void test5() {
	Vector<string> v;
	v.push_back("123");
	v.push_back("456");
	v.push_back("78");
	v.push_back("9");

	printV(v);
}

运行结果如下 :

在这里插入图片描述

OK , 到这里就结束了

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

殇&璃

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

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

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

打赏作者

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

抵扣说明:

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

余额充值