c++ - vector容器常用接口模拟实现

news2024/10/5 6:27:46

文章目录

    • 一、成员变量
    • 二、常用迭代器接口模拟实现
    • 三、一些常用接口模拟
    • 四、默认成员函数
    • 五、功能测试


一、成员变量

我们通过在堆上申请一个数组空间来进行储存数据,我们的成员变量是三个指针变量,分别指向第一个位置、最后储存有效位置的下一个位置以及数组空间的最后位置的下一个位置。

//为了与stl保持一致使用重命名
typedef T* iterator;
typedef const T* const_iterator;

iterator _start = nullptr;
iterator _finish = nullptr;
iterator _end_of_storage = nullptr;

在这里插入图片描述

二、常用迭代器接口模拟实现

直接返回数组的开始位置和有效数据的下一个位置即可。

//迭代器
//第一个位置
iterator begin()
{
	return  _start;
}

//有效数据的下一个位置
iterator end()
{
	return _finish;
}

//重载 const
const iterator begin() const
{
	return  _start;
}

const iterator end() const
{
	return _finish;
}

三、一些常用接口模拟

(1)size
求有效数据的个数,用结束位置下一个位置的指针减去第一个位置的指针就是有效数据的个数了。

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

(2)capacity
求容器的容量,用数组空间的最后位置的下一个位置的指针减去第一个位置的指针就是容量的大小了。

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

(3)重载[]

//重载 []
T operator[](size_t i)
{
	//保证i位置符合
	assert(i < size());

	return  *(_start + i);
}

const T operator[](size_t i) const
{
	//保证i位置符合
	assert(i < size());

	return  *(_start + i);
}

(4)reserve
预留空间,一般用于扩容,这里需要手动扩容,再拷贝数据,最后再改变成员变量的指向。

void reserve(size_t n)
{
	//先保存有效数据个数
	size_t _size = size();

	//判断是否需要扩容
	if (n > _size)
	{
		//手动申请空间
		iterator tmp = new T[n];
		//拷贝数据到新空间
		for (size_t i = 0; i < _size; i++)
		{
			*(tmp + i) = *(_start + i);
		}
		//释放掉原来的空间
		delete[] _start;

		//重新改变位置
		_start = tmp;
		_finish = _start + _size;
		_end_of_storage = _start + n;
	}
}

注意:
a.要先保存 size,因为在重新指向时需要_start第一个重新指向新空间,如果此时再去调用size()函数的话,返回的size是不确定的。最终导致_finish 指向错误。

b.就是不能使用memcpy函数进行拷贝,因为memcpy是浅拷贝,当出现数据是自定义类型而且也是需要申请资源的话就会出现重复释放等问题。如数据储存的是string类型。
在这里插入图片描述
所以需要使用深拷贝。
在这里插入图片描述
(5)clear
清空,将_start = _finish即可。

void clear()
{
	_finish = _start;
}

(6)insert
在迭代器位置前插入一个元素,并返回新的position位置。
迭起器失效问题:
插入一个位置可能会扩容,扩容就会导致position位置失效,所以如果需要扩容就先保存好 position 相对 _start的位置。

iterator insert(iterator position, const T val)
{
	//迭代器位置
	assert(position >= _start);
	assert(position <= _finish);

	//空间是否够
	if (_finish == _end_of_storage)
	{	
		//保存相对位置
		size_t _size = position - _start;

		size_t newcapacity = capacity() == 0 ? 4 : capacity() * 2;
		reserve(newcapacity);
		
		//重新获取position位置
		position = _start + _size;
	}
	
	//往后移动一位
	iterator end = _finish;
	while (end > position)
	{
		*end = *(end-1);
		--end;
	}
	
	*position = val;
	++_finish;
	
	return position;
}

(7)push_back
尾插入一个元素,这里直接调用insert即可。

void push_back(T x)
{
	insert(_finish, x);
}

(8)erase
删除position位置,返回position位置的值。
这里也可能会存在迭代器失效问题,如缩容(这里不考虑)、最后一个位置。

//删除迭代器
iterator erase(iterator position)
{
	//符合位置
	assert(position >= _start);
	assert(position < _finish);

	//覆盖掉position
	iterator end = position;
	while (end < _finish - 1)
	{
		*end = *(end + 1);
		++end;
	}

	--_finish;
	//这里返回空,因为position已经失效了
	if (position == _finish + 1)
	{
		return nullptr;
	}

	return position;
}

(9)empty
是否空。

bool empty() const
{
	return size() == 0;
}

(10)pop_back
尾删,这里直接调用erase()即可。

void pop_back()
{
	if (empty())
		return;
		
	erase(_finish-1);
}

(11)resize
重新定义大小,n小于实际大小就缩小,n大于实际大小就用val来填充。

//重新定义大小

void resize(size_t n, T val = T())
{

	if (n > capacity())
	{
		reserve(n);
	}

	size_t _size = size();
	if (n > _size)
	{
		for (int i = _size; i < n; i++)
		{
		//直接尾插
			push_back(val);
		}
	}
	else
	{
		_finish = _start + n;
	}

}

(12)swap
交换两个容器。

void swap(vector<T>& x)
{
	//利用std库中的将指针位置交换即可
	std::swap(_start, x._start);
	std::swap(_finish, x._finish);
	std::swap(_end_of_storage, x._end_of_storage);
}

四、默认成员函数

(1)默认构造
无参构造,给了有默认值,这里不用初始化。

vector() {};

用n个val来初始化

vector(size_t n, T val = T())
{
	//先预留空间,这样就不用扩容了
	reserve(n);

	int i = 0;
	while (i < n)
	{
	//尾插
		push_back(val);
		++i;
	}
};

拷贝构造
这里要用深拷贝

vector(vector<T> &x) 
{
	//先预留空间,这样就不用扩容了
	reserve(x.capacity());
	
	//直接将获取的数据插入即可,不用memcpy这种直接拷贝
	vector<T>::iterator it = x.begin();
	while (it != x.end())
	{
	
		push_back(*it);
		++it;
	}
};

用迭代器区间构造,因为可以用其他的容器来初始化,所以这里再用一个模板。

template <class InputIterator>
vector(InputIterator first, InputIterator last)
{
	//先预留空间,这样就不用扩容了
	reserve(last - first);

	while (first < last)
	{
		push_back(*first);
		++first;
	}
}

因为迭代器区间的构造函数与用n个val来初始化构造会冲突。
在这里插入图片描述

为了解决这个问题,使用一个具体一点的n个val来初始化的构造函数

vector(int n, T val = T())
{
	reserve(n);

	int i = 0;
	while (i < n)
	{
		push_back(val);
		++i;
	}
};

(2)析构函数

~vector()
{
	//释放
	delete[]_start;
	_start = _finish = _end_of_storage = nullptr;
}

(3)重载 =

vector<T>& operator=(vector<T> x)
{
	//利用交换函数,x出了该函数作用域就会销毁,所以不用我们手动销毁原来空间了
	swap(x);
	return *this;
}

五、功能测试

(1)内置类型

void test01()
{
	//n个val
	xu::vector<int> v1(2,1);
	for (int i = 0; i < v1.size(); i++)
	{
		cout << v1[i] << " ";
	}
	cout << endl;

	//区间
	xu::vector<int> v2(v1.begin(), v1.end());
	for (int i = 0; i < v1.size(); i++)
	{
		cout << v2[i] << " ";
	}
	cout << endl;

	//赋值
	xu::vector<int> v3;
	v3 = v1;
	for (int i = 0; i < v1.size(); i++)
	{
		cout << v3[i] << " ";
	}
	cout << endl;
	

	v1.push_back(2);
	v1.insert(v1.begin(), 0);
	for (int i = 0; i < v1.size(); i++)
	{
		cout << v1[i] << " ";
	}
	cout << endl;
	v1.pop_back();
	v1.erase(v1.begin());
	for (int i = 0; i < v1.size(); i++)
	{
		cout << v1[i] << " ";
	}
	cout << endl;

	v1.resize(10, 5);
	for (int i = 0; i < v1.size(); i++)
	{
		cout << v1[i] << " ";
	}
	cout << endl;


}

在这里插入图片描述

(2)自定义类型

void test02()
{
	//n个val
	xu::vector<string> v1(2, "aa");
	for (int i = 0; i < v1.size(); i++)
	{
		cout << v1[i] << " ";
	}
	cout << endl;

	//区间
	xu::vector<string> v2(v1.begin(), v1.end());
	for (int i = 0; i < v1.size(); i++)
	{
		cout << v2[i] << " ";
	}
	cout << endl;

	//赋值
	xu::vector<string> v3;
	v3 = v1;
	for (int i = 0; i < v1.size(); i++)
	{
		cout << v3[i] << " ";
	}
	cout << endl;


	v1.push_back("bb");
	v1.insert(v1.begin(), "cc");
	for (int i = 0; i < v1.size(); i++)
	{
		cout << v1[i] << " ";
	}
	cout << endl;
	v1.pop_back();
	v1.erase(v1.begin());
	for (int i = 0; i < v1.size(); i++)
	{
		cout << v1[i] << " ";
	}
	cout << endl;

	v1.resize(10, "ee");
	for (int i = 0; i < v1.size(); i++)
	{
		cout << v1[i] << " ";
	}
	cout << endl;

}

在这里插入图片描述

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

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

相关文章

OpenMV学习笔记1——IDE安装与起步

目录 一、OpenMV IDE下载 二、OpenMV界面 三、Hello World&#xff01; 四、将代码烧录到OpenMV实现脱机运行 五、插SD卡&#xff08;为什么买的时候没送&#xff1f;&#xff09; 一、OpenMV IDE下载 浏览器搜索OpenMV官网&#xff0c;进入后点击“立即下载”&#xff0…

org.json下载方法

介绍org.json下载的一些方法。 工具/原料 浏览器 方式一 在百度上搜索org.json&#xff0c;点击第一个搜索结果。进入JSON网站后&#xff0c;可以看到有各种语言版本的json工具包&#xff0c;选择JSON-java。 点击JSON-java后页面跳转到GitHub上&#xff0c;在该网页上点击…

吉林大学软件工程易错题

1.【单选题】软件工程方法是&#xff08; &#xff09;。 A、为开发软件提供技术上的解决方法 &#xff08;软件工程方法 &#xff09; B、为支持软件开发、维护、管理而研制的计算机程序系统&#xff08;软件工程工具&#xff09; …

Linux基础(四):Linux系统文件类型与文件权限

各位看官&#xff0c;好久不见&#xff0c;在正式介绍Linux的基本命令之前&#xff0c;我们首先了解一下&#xff0c;关于文件的知识。 目录 一、文件类型 二、文件权限 2.1 文件访问者的分类 2.2 文件权限 2.2.1 文件的基本权限 2.2.2 文件权限值的表示方法 三、修改文…

爬虫实训案例:中国大学排名

近一个月左右的时间学习爬虫&#xff0c;在用所积累的知识爬取了《中国大学排名》这个网站&#xff0c;爬取的内容虽然只是可见的文本&#xff0c;但对于初学者来说是一个很好的练习。在爬取的过程中&#xff0c;通过请求数据、解析内容、提取文本、存储数据等几个重要的内容入…

MT3039 山脉

思路&#xff1a; 往右看能看到山顶&#xff0c;可以看成找第一个比当前元素>的元素&#xff0c;即构造单调递减栈。 例子&#xff1a; 7 5 3 4 1. 7入栈: 7 2. 5入栈: 7 5 ansans1(1是指有1个元素&#xff08;7&#xff09;可以看到5) 3. 3入栈: 7 5 3 ansans2(2是指…

使用神经实现路径表示的文本到向量生成

摘要 矢量图形在数字艺术中得到广泛应用&#xff0c;并受到设计师的青睐&#xff0c;因为它们具有可缩放性和分层特性。然而&#xff0c;创建和编辑矢量图形需要创造力和设计专业知识&#xff0c;使其成为一项耗时的任务。最近在文本到矢量&#xff08;T2V&#xff09;生成方面…

单例模式中的 双判断锁 问题、单例模式的资源问题

》》》Lazy 不存在高并发问题&#xff0c;lazy已经解决了。 CLR 类执行的顺序 静态变量初始化 1次静态构造函数 1次实例变量初始化基类静态变量初始化 1次基类静态构造函数 1次基类实例变量初始化基类实例构造函数实例构造函数 》》》 创建单例模式 好多种 1&#xff0c;静态…

丰田精益生产的模板

丰田精益生产&#xff0c;也被称为丰田生产方式&#xff08;Toyota Production System, TPS&#xff09;&#xff0c;是一套完整的生产和管理系统&#xff0c;其核心目标是最大化效率、消除浪费&#xff0c;并通过持续改进来提升产品质量。 学习优秀企业 学习福特 丰田精益生产…

文件流下载优化:由表单提交方式修改为Ajax请求

如果想直接看怎么写的可以跳转到 解决方法 节&#xff01; 需求描述 目前我们系统导出文件时&#xff0c;都是通过表单提交后&#xff0c;接收文件流自动下载。但由于在表单提交时没有相关调用前和调用后的回调函数&#xff0c;所以我们存在的问题&#xff0c;假如导出数据需…

【数据分析】Numpy和Pandas库基本用法及实例--基于Japyter notebook实现

各位大佬好 &#xff0c;这里是阿川的博客 &#xff0c; 祝您变得更强 个人主页&#xff1a;在线OJ的阿川 大佬的支持和鼓励&#xff0c;将是我成长路上最大的动力 阿川水平有限&#xff0c;如有错误&#xff0c;欢迎大佬指正 承接上篇的博客 数据分析—技术栈和开发环境搭…

【Django】从零开始学Django(持续更新中)

PyCharm的版本必须为专业版&#xff0c;社区版不具备Web开发功能的。 一. Django建站基础 Django采用MTV的框架模式&#xff0c;即模型(Model)、模板(Template)和视图(Views)&#xff0c;三者之间各自负责不同的职责。 ●模型&#xff1a;数据存取层&#xff0c;处理与数据相关…

Macos14.4 安装MySQL5.7

文章目录 前言一、MySQL介绍二、安装步骤1.下载2.安装3.配置1.进入系统设置2.启动服务3.配置环境变量4.修改密码 FAQ1.双击安装时提示&#xff1a;检测恶意软件&#xff0c;无法打开2.修改环境变量文件提示&#xff1a;readonly option is set (add ! to override)文件权限不足…

go 微服务框架 kratos 日志库使用方法及原理探究

一、Kratos 日志设计理念 kratos 日志库相关的官方文档&#xff1a;日志 | Kratos Kratos的日志库主要有如下特性&#xff1a; Logger用于对接各种日志库或日志平台&#xff0c;可以用现成的或者自己实现Helper是在您的项目代码中实际需要调用的&#xff0c;用于在业务代码里…

学习图形推理

学习图形推理 1.位置规律1.1平移1.2翻转、旋转2.样式规律2.1加减异同2.2黑白运算3.属性规律3.1对称性3.2曲直性3.3开闭性4.数量规律4.1面4.2线数量4.3笔画数4.4点数量4.5素数量5.空间重构5.1相对面5.2相邻面-公共边5.3相邻面-公共点5.4相邻面-画边法题型 一组图:从左往右找规律…

SQL——SELECT相关的题目

目录 197、上升的温度 577、员工奖金 586、订单最多的客户 596、超过5名学生的课 610、判断三角形 620、有趣的电影 181、超过经理收入的员工 1179、重新格式化部门表 1280、学生参加各科测试的次数 1068、产品销售分析I 1075、项目员工I 1084、销售分析III 1327、列出指…

LLM-Llama在 MAC M1上体验Llama.cpp和通义千问Qwen 1.5-7B

Llama.cpp的主要目标是在各种硬件上&#xff08;本地和云端&#xff09;实现LLM推断&#xff0c;同时保持最小的设置和最先进的性能。 纯C/C实现&#xff0c;没有任何依赖关系Apple芯片是一级的支持对象 - 通过ARM NEON、Accelerate和Metal框架进行优化对x86架构的AVX、AVX2和…

后端之路第二站(正片)——SprintBoot之:分层解耦

很抽象&#xff0c;我自己也不好理解&#xff0c;仅作为一个前端转后端的个人理解 一、先解释一个案例&#xff0c;以这个案例来分析“三层架构” 这里我先解释一下黑马程序员里的这个案例&#xff0c;兄弟们看视频的可以跳过这节课&#xff1a;Day05-08. 请求响应-响应-案例_…

U-Mail邮件系统反垃圾解决方案,彻底解决垃圾邮件

随着互联网的普及和电子邮件的广泛应用&#xff0c;垃圾邮件已成为一种严重的网络污染。首先&#xff0c;垃圾邮件占用了大量的网络带宽&#xff0c;导致正常邮件的传输受阻&#xff0c;严重影响了用户的使用体验。其次&#xff0c;垃圾邮件中的恶意链接和欺诈信息可能导致用户…

day34 贪心算法 455.分发饼干 376. 摆动序列

贪心算法理论基础 贪心的本质是选择每一阶段的局部最优&#xff0c;从而达到全局最优。 贪心一般解题步骤&#xff08;贪心无套路&#xff09;&#xff1a; 将问题分解为若干个子问题找出适合的贪心策略求解每一个子问题的最优解将局部最优解堆叠成全局最优解 455.分发饼干 …