C++ vector的模拟实现

news2025/1/22 21:12:36

一 vector的大致框架

1.1 框架

 vector的成员变量不再是我们熟悉的size,capacity,而是变成了功能一致的三个指针:_start,_finish,_endofstorage,三个指针的作用如下:

同时,因为其本身指针的特性,其迭代器也是返回其内部的指针就可以了,因此我们可以直接定义迭代器。

 大致框架如下:
 

namespace My
{
	template<class T>
	class vector
	{
	public:
        typedef T* iterator;
        typedef const T* const_iterator;
	private:
		iterator _start;
		iterator _finish;
		iterator _endofstorage;
	};
}

1.2 默认构造函数 

 实际上,我们可以在底层初始化给三个指针都赋值为nullptr,这样默认构造就可以什么都不敢,并且在初始化时就全都赋值为nullptr,可以使代码更加简练。

代码如下:
 

	//构造函数
	vector() {
	}
private:
		iterator _start = nullptr;
		iterator _finish = nullptr;
		iterator _endofstorage = nullptr;

1.3 析构函数

delete掉空间,将三个指针再次赋值为nullptr即可。

 

	//析构 
	~vector() {
		delete[] _start; //注意一定要加[],因为有可能存的值是一个数组
		_start = _finish = _endofstorage = nullptr;
	}

二 vector的容量操作

2.1 size函数

 一般的容器,我们都有size和capacity两个函数 来获取其容器大小,在vector中我们也有这两个函数,这里我们通过 指针-指针的差值来 计算其实际值。

 代码如下:
 

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

2.2 capacity函数

 代码如下:

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

2.3 reserve函数

  在库中,reserve 修改容量时,size 是不会发生变化的(因为有效元素个数不变),若空间容量扩大则 capacity 会相应的进行扩大,若空间容量缩小时 capacity 是不会发生变化的。

  reserve的思想就是把原来的空间舍弃掉,重新开一段大空间

代码如下:
 

	//扩容
	void reserve( size_t n) {
		if (n > capacity()) {
			//开始扩容
			T* tmp = new T[n];
			size_t sz = size();//提前算出size(),用来把原值赋值给新空间
			if (_start) {
				for (size_t i = 0; i < sz; i++) {
					tmp[i] = _start[i];
				}
				delete[] _start;//避免内存泄漏
			}
			//指针指向新空间
			_start = tmp;
			_finish = _start + sz;
			_endofstorage = _start + n;
		}
	}

2.4 resize函数

 resize 修改有效元素个数时,size 会进行相应的扩大或缩小变化,capacity 不一定会变化(有效元素增多时,若容量足够则capacity不变,否则会扩容;有效元素减少时,capacity 不变)

 resize的两个操作如下:

  • 1.如果n小于当前的size,则数据个数缩小到n
  • 2.如果n大于于当前的size,则说明空间不够需要增容,并且将size扩大到n

  注意,这里val得用T的默认构造。 

 代码示例:

	//resize
	void resize(size_t n, const T& val = T()) {
		if (n <= size()) {
			_finish = _start + n;
		}
		else {
			reserve(n);
			while (_finish < _start + n) {
				*_finish = val;
				++_finish;
			}
		}
	}

三迭代器 

  vector的迭代器就是原生指针,因此直接返回指针即可:
 

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

四 元素修改函数

4.1 insert函数

 在指定pos位置进行插入元素

 但是这里却有一个很坑的点,那就是不小心写出来的代码就会引起迭代器失效的问题。

  因为插入后,我们可能会发生扩容,pos指针会失效,这里我们先记录一下pos位置和起始位置的差值,扩容后让pos指针指向新空间。

	void insert(iterator pos, const T& val) {
		assert(pos >= _start);
		assert(pos <= _finish);
		if (_finish == _endofstorage) {
			size_t len = pos - _start; 
			reserve(capacity() == 0 ? 4 : capacity() * 2);
			pos = _start + len;
		}
		iterator end = _finish - 1;
		while (end >= pos) {
			*(end + 1) = *end;
			--end;
		}
		*pos = val;
		++_finish;
	}

但要注意,这样的话,外部的pos指针怎么办呢?

这里是值传递,形参的改变是不会改变实参的,所以说外面的pos依旧没有实现更新操作,依旧会失效。

正确写法是通过返回值去解决问题的,返回的是迭代器,即新插入元素的位置。

正确代码为:
 

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

4.2 erase()

  这里主要的就是从后往前删除的,这点需要大家注意一下。

  同时,erase也会发生迭代器失效问题,因此要返回值避免迭代器失效。

代码如下:
 

	iterator erase(iterator pos) {
		assert(pos >= _start);
		assert(pos < _finish);
		iterator it = pos + 1;
		while (it < _finish) {
			*(it - 1) = *it;
			++it;
		}
		--_finish;
		return pos;
	}

4.3 push_back()

插入尾部元素,直接复用insert.

void push_back(const T& val) {
	insert(end(), val);
}

4.4 pop_back() 

删除尾部元素,直接把_finish--就好。

	void pop_back()
	{
		assert(_start); //注意判空
		--_finish;
	}

五 元素访问

 5.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];
	}

六 其它函数 

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

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

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

//拷贝构造
vector(const vector<T>& v) {
	reserve(v.capacity());
	for (auto& x : v) {
		push_back(x);
	}
}

//移动构造
vector(vector<T>&& v) {
	swap(v);
}

//赋值重载
vector<T>& operator=(const vector<T>& v) {
	vector<T> tmp(v);
	swap(tmp);
	return *this;
}
//移动赋值
vector<T>& operator=(vector<T>&& v) {
	swap(v);
	return *this;
}

七 完整代码和测试代码

 

#pragma once
#include<iostream>
#include<cassert>

using namespace std;

namespace My {

	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() {
		}


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

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

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

		//拷贝构造
		vector(const vector<T>& v) {
			reserve(v.capacity());
			for (auto& x : v) {
				push_back(x);
			}
		}

		//移动构造
		vector(vector<T>&& v) {
			swap(v);
		}

		//赋值重载
		vector<T>& operator=(const vector<T>& v) {
			vector<T> tmp(v);
			swap(tmp);
			return *this;
		}
		//移动赋值
		vector<T>& operator=(vector<T>&& v) {
			swap(v);
			return *this;
		}

		//扩容
		void reserve( size_t n) {
			if (n > capacity()) {
				//开始扩容
				T* tmp = new T[n];
				size_t sz = size();//提前算出size(),用来把原值赋值给新空间
				if (_start) {
					for (size_t i = 0; i < sz; i++) {
						tmp[i] = _start[i];
					}
					delete[] _start;//避免内存泄漏
				}
				//指针指向新空间
				_start = tmp;
				_finish = _start + sz;
				_endofstorage = _start + n;
			}
		}

		//resize
		void resize(size_t n, const T& val = T()) {
			if (n <= size()) {
				_finish = _start + n;
			}
			else { 
				reserve(n);
				while (_finish < _start + n) {
					*_finish = val;
					++_finish;
				}
			}
		}

		iterator insert(iterator pos, const T& val) {
			assert(pos >= _start);
			assert(pos <= _finish);
			if (_finish == _endofstorage) {
				size_t len = pos - _start;
				reserve(capacity() == 0 ? 4 : capacity() * 2);
				pos = _start + len;
			}
			iterator end = _finish - 1;
			while (end >= pos) {
				*(end + 1) = *end;
				--end;
			}
			*pos = val;
			++_finish;
			return pos;
		}
		void push_back(const T& val) {
			insert(end(), val);
		}

		iterator erase(iterator pos) {
			assert(pos >= _start);
			assert(pos < _finish);
			iterator it = pos + 1;
			while (it < _finish) {
				*(it - 1) = *it;
				++it;
			}
			--_finish;
			return pos;
		}
		void pop_back()
		{
			assert(_start); //注意判空
			--_finish;
		}

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

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


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

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


		//析构 
		~vector() {
			delete[] _start; //注意一定要加[]
			_start = _finish = _endofstorage = nullptr;
		}

	private:
		iterator _start = nullptr;
		iterator _finish = nullptr;
		iterator _endofstorage = nullptr;

	};


	void test_vector1()
	{
		vector<int> v;
		v.push_back(1);
		v.push_back(2);
		v.push_back(3);
		v.push_back(4);
		v.push_back(5);

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

		vector<int>::iterator it = v.begin();
		while (it != v.end())
		{
			*it *= 10;
			cout << *it << " ";
			++it;
		}
		cout << endl;

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

	void test_vector2()
	{
		int i = 0;
		int j(1);
		int k = int(2);

		vector<int*> v1;
		v1.resize(10);

		vector<string> v2;
		//v2.resize(10, string("xxx"));
		v2.resize(10, "xxx");

		for (auto e : v1)
		{
			cout << e << " ";
		}
		cout << endl;

		for (auto e : v2)
		{
			cout << e << " ";
		}
		cout << endl;
	}

	void test_vector3()
	{
		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);
		v.push_back(7);

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

		vector<int>::iterator it = v.begin() + 2;
		v.insert(it, 30);
		for (auto e : v)
		{
			cout << e << " ";
		}
		cout << endl;

		//v.insert(v.begin(), 30);
		v.insert(v.begin() + 3, 30);
		for (auto e : v)
		{
			cout << e << " ";
		}
		cout << endl;
	}

	void test_vector4()
	{
		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);
		v.push_back(7);

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

		auto pos = v.begin();
		v.erase(pos);

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

		v.erase(v.begin() + 3);
		for (auto e : v)
		{
			cout << e << " ";
		}
		cout << endl;
	}

	//void test_vector5()
	//{
	//	// 1 2 3 4 5
	//	// 1 2 3 4 5 6
	//	// 2 2 3 4 5
	//	std::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);

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

	//	auto it = v.begin();
	//	while (it != v.end())
	//	{
	//		// vs2019进行强制检查,erase以后认为it失效了,不能访问,访问就报错
	//		if (*it % 2 == 0)
	//		{
	//			v.erase(it);
	//		}

	//		++it;
	//	}

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

	void test_vector5()
	{
		//std::vector<int> v;
		vector<int> v;

		v.push_back(1);
		v.push_back(2);
		v.push_back(3);
		v.push_back(4);
		v.push_back(4);
		v.push_back(5);
		v.push_back(6);

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

		auto it = v.begin();
		while (it != v.end())
		{
			if (*it % 2 == 0)
			{
				it = v.erase(it);
			}
			else
			{
				++it;
			}
		}

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

	void test_vector6()
	{
		vector<string> v;
		v.push_back("111111111111111111111");
		v.push_back("111111111111111111111");
		v.push_back("111111111111111111111");
		v.push_back("111111111111111111111");
		v.push_back("111111111111111111111");


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


	void test_vector7()
	{
		vector<int> v1;
		v1.push_back(1);
		v1.push_back(1);
		v1.push_back(1);
		v1.push_back(1);
		v1.push_back(1);

		vector<int> v2(v1);

		for (auto e : v1)
		{
			cout << e << " ";
		}
		cout << endl;

		for (auto e : v2)
		{
			cout << e << " ";
		}
		cout << endl;

		vector<int> v3;
		v3.push_back(10);
		v3.push_back(20);
		v3.push_back(30);
		v3.push_back(40);

		v1 = v3;

		for (auto e : v1)
		{
			cout << e << " ";
		}
		cout << endl;
	}

	void test_vector8()
	{
		//vector<int> v0(10, 0);
		vector<string> v1(10, "xxxx");

		for (auto e : v1)
		{
			cout << e << " ";
		}
		cout << endl;

		vector<int> v2;
		v2.push_back(10);
		v2.push_back(20);
		v2.push_back(30);
		v2.push_back(40);

		vector<int> v3(v2.begin(), v2.end());

		string str("hello world");
		vector<int> v4(str.begin(), str.end());
		for (auto e : v3)
		{
			cout << e << " ";
		}
		cout << endl;

		for (auto e : v4)
		{
			cout << e << " ";
		}
		cout << endl;
	}


}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1330380.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

行为型设计模式(四):中介模式 命令模式

中介模式 Mediator 1、什么是中介模式 中介模式用于减少对象之间的直接通信&#xff0c;让系统可以更加松耦合。定义了一个中介者对象&#xff0c;该对象封装了一系列对象的交互&#xff0c;使得对象之间不再直接相互引用&#xff0c;而是通用这个中介者对象进行通信。 2、为…

清风数学建模笔记-插值算法

内容&#xff1a;插值算法 概念&#xff1a; 二.种类 1.牛顿插值法&#xff0c;拉格朗日插值法&#xff0c;两者容易出现龙格现象 2.分段线性插值&#xff1a;与上面两种相比要更好一些,原理是两线之间构成一条直线&#xff0c;在这条直线上插值&#xff0c;除此之外还有分段…

10、基于LunarLander登陆器的Dueling DDQN强化学习(含PYTHON工程)

10、基于LunarLander登陆器的Dueling DDQN强化学习&#xff08;含PYTHON工程&#xff09; LunarLander复现&#xff1a; 07、基于LunarLander登陆器的DQN强化学习案例&#xff08;含PYTHON工程&#xff09; 08、基于LunarLander登陆器的DDQN强化学习&#xff08;含PYTHON工程…

sql_lab之sqli中的宽字节注入(less32)

宽字节注入&#xff08;less-32&#xff09; 1.判断注入类型 http://127.0.0.3/less-32/?id1 http://127.0.0.3/less-32/?id1 出现 \’ 则证明是宽字节注入 2.构成闭环 http://127.0.0.3/less-32/?id1%df -- s 显示登录成功则构成闭环 3.查询字段数 http://127.0.0.3/…

TypeScript前端学习(三)

前言 继续分享TypeScript学习笔记&#xff0c;大佬请绕行。 一、函数参数 function func1(a, b, c) {console.log("-----" a, b, c); } func1("a", "b", "c"); function func2(a, b, c) {console.log("---可变参数--" a,…

并发控制工具类CountDownLatch、CyclicBarrier、Semaphore

并发控制工具类CountDownLatch、CyclicBarrier、Semaphore 1.CountDownLatch 可以使一个或多个线程等待其他线程各自执行完毕后再执行。 CountDownLatch 是多线程控制的一种工具&#xff0c;它被称为 门阀、 计数器或者闭锁。这个工具经常用来用来协调多个线程之间的同步&…

【go-zero】 go-zero API 如何接入 Nacos 被 java 服务调用 | go集成java服务

一、场景 外层使用的是springcloud alibaba 这一套java的分布式架构 然后需要接入go-zero的api服务 这里我们将对api服务接入Nacos进行一个说明 二、实战 1、package 因为使用的是go-zero框架 这里我们会优先使用go-zero生态的包 github 包如下: github.com/nacos-group/naco…

Qt通用属性工具:随心定义,随时可见(一)

一、开胃菜&#xff0c;没图我说个DIAO 先不BB&#xff0c;给大家上个效果图展示下&#xff1a; 上图我们也没干啥&#xff0c;几行代码&#xff1a; #include "widget.h" #include <QApplication> #include <QObject> #include "QtPropertyEdit…

leetcode中的状态机类型的题目

1 总结 2 LC57. 插入区间 2.1 解析 先是要确定新区间插入到哪一个位置&#xff08;也有可能&#xff09;&#xff0c;插入后需要确定这个区间是否涉及到合并问题。 所以我们可以设计一个flag变量&#xff0c;确定区间是否插入&#xff0c;插入完成则进行到区间合并阶段。 2.…

工业信息采集平台的五大核心优势

关键字&#xff1a;工业信息采集平台,蓝鹏数据采集系统,蓝鹏测控系统, 生产管控系统, 生产数据处理平台,MES系统数据采集, 蓝鹏数据采集平台通过实现和构成其他工业数据信息平台的一级设备进行通讯&#xff0c;从而完成平台之间的无缝对接。这里我们采用的最多的方式是和PLC进行…

AI“百模大战”现状:向垂直、B端谋场景,算力仍是主要制约因素

文章目录 每日一句正能量前言AI&#xff08;人工智能&#xff09;大模型正“飞入”百姓家和行业中。向垂直、B端谋场景算力仍是主要制约因素构建“数据-模型-应用”飞轮后记 每日一句正能量 我们必须在失败中寻找胜利&#xff0c;在绝望中寻求希望。 前言 在当前快速发展的人工…

Docker与容器化安全:漏洞扫描和安全策略

容器化技术&#xff0c;特别是Docker&#xff0c;已经成为现代应用程序开发和部署的关键工具。然而&#xff0c;容器化环境也面临着安全挑战。为了保障容器环境的安全性&#xff0c;本文将介绍如何进行漏洞扫描、制定安全策略以及采取措施来保护Docker容器。我们将提供丰富的示…

pvk2pfx.exe makecert.exe 文件路径

文件路径 C:\Program Files (x86)\Windows Kits\10\bin\XXXXX\x86

CSS新手入门笔记整理:CSS3弹性盒模型

特点 子元素宽度之和小于父元素宽度&#xff0c;所有子元素最终的宽度就是原来定义的宽度。子元素宽度之和大于父元素宽度&#xff0c;子元素会按比例来划分宽度。在使用弹性盒子模型之前&#xff0c;必须为父元素定义“display:flex;”或“display:inline-flex;”。 弹性盒子…

Chart.js:灵活易用的图表库 | 开源日报 No.121

chartjs/Chart.js Stars: 61.3k License: MIT Chart.js 是一个简单而灵活的 JavaScript 图表库&#xff0c;适用于设计师和开发者。 灵活性&#xff1a;Chart.js 提供了丰富多样的图表类型和配置选项&#xff0c;使用户能够根据自己的需求创建各种定制化的图表。易用性&#…

【Python必做100题】之第二十六题(小球反弹问题)

题目&#xff1a;一小球从100米高度自由落体落下&#xff0c;每次落地后反跳回原来高度的一半再落下&#xff0c;求它在第10次落地时&#xff0c;共经过多少米&#xff1f;第10次反弹多高&#xff1f; 思路&#xff1a;初始为100米&#xff0c;落下反弹为原来的一半&#xff1…

Leetcode 剑指 Offer II 058. 我的日程安排表 I

题目难度: 中等 原题链接 今天继续更新 Leetcode 的剑指 Offer&#xff08;专项突击版&#xff09;系列, 大家在公众号 算法精选 里回复 剑指offer2 就能看到该系列当前连载的所有文章了, 记得关注哦~ 题目描述 请实现一个 MyCalendar 类来存放你的日程安排。如果要添加的时间内…

基于ip地址通过openssl生成自签名证书

最近在配置geo的时候&#xff0c;客户说自己使用的是自签证书&#xff0c;然后是通过ip地址和端口的方式访问gitlab&#xff0c;比较好奇这块&#xff0c;因此对证书的生成和使用做了一些整理&#xff0c;对此网上关于这部分资料也很多&#xff0c;不过作为记录&#xff0c;也算…

模型推理加速系列 | 08:TensorRT-LLM助力LLM高性能推理

引言 ​ 日暮苍山远&#xff0c;天寒白屋贫。Created by DALLE 3 小伙伴们好&#xff0c;我是《小窗幽记机器学习》的小编&#xff1a;卖汤圆的小女孩&#xff0c;今天是冬至&#xff0c;祝福小伙伴们幸福安康吧。紧接前文&#xff1a; 万字长文细说ChatGPT的前世今生 Llam…

DBeaver中使用外部格式化程序对进行sql格式化

本文介绍了如何在DBeaver中使用pgFormatter、sqlprase、sqlformatter等外部格式化程序对sql进行格式化。 目录 一、pgFormatter 1.准备工作 2.DBeaver中进行配置 二、sqlprase 1.准备工作 2.在DBeaver中配置 三、sql-formatter 1.准备工作 2.在DBeaver中配置 一、pgF…