C++初阶之vector的使用和模拟实现

news2024/11/18 5:26:43

个人主页:点我进入主页

专栏分类:C语言初阶  C语言进阶  数据结构初阶    Linux    C++初阶    算法

欢迎大家点赞,评论,收藏。

一起努力,一起奔赴大厂

目录

一.成员变量

二.默认构造

三.size和capacity函数

3.1库中的size函数和capacity函数

3.2模拟实现size和capacity

四.扩容

4.1库中的扩容函数解析

4.2模拟实现扩容函数

4.3resize

五.push_back

5.1库中的push_back

5.2模拟实现push_back

六.迭代器

6.1库中迭代器

6.2模拟

七.insert以及push_back的复用

7.1库中的insert

7.2模拟实现inser以及迭代器失效问题

7.3push_back复用insert

八.删除

8.1pop_back

8.1.1库中的pop_back

8.1.2模拟实现pop_back

8.2erase

8.2.1库中的erase

8.2.1模拟实现erase以及迭代器失效问题

九.拷贝构造

十.再次分析默认构造

十一.赋值构造

十二.析构


一.成员变量

iterator _start = nullptr;
iterator _finish = nullptr;
iterator _endofstorage = nullptr;

        在这里我们采用给缺省值,这样可以省去默认构造,成员的类型是迭代器,在后面会分析迭代器是什么样子,在这里我们将他看成指针就可以了。

二.默认构造

vector()
{}

由于我们在声明成员变量时采用了给缺省值,所以不需要我们进行默认构造。其余的默认构造需要使用迭代器,后面会分析。

三.size和capacity函数

3.1库中的size函数和capacity函数

        由于我们的成员是用指针进行写的,所以我们需要得到它的大小和空间,我们需要通过函数来获取,库中给的是这两个函数,由于有const版本和非const这两个,又由于不需要修改数据,所以全部使用const版本。

3.2模拟实现size和capacity

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

我们直接利用指针相减来获取对应的大小和内存。 

四.扩容

4.1库中的扩容函数解析

参数是我们当前的容量,当满是就扩容,每一次插入时都需要进行一次检查

4.2模拟实现扩容函数

void reserve(size_t n)
{
	size_t oldsize = size();
	T* tmp = new T[n];
	//memcpy(tmp, _start, size() * sizeof(T));
	T* p = tmp;
	for (int i = 0; i < oldsize; i++)
	{
		*p = *(_start + i);
		p++;
	}
	delete[] _start;

	_start = tmp;
	_finish = _start + oldsize;
	_endofstorage = _start + n;
}

        当我们的vector是内置类型时可以使用memcpy进行拷贝,因为它只需要进行浅拷贝,但是我们的类型时自定义类型,例如string或者vector等需要深拷贝的类型由于memcpy会进行浅拷贝,所以我们需要进行深拷贝,这个时候我们的解决方法就是直接进行变量插入。

4.3resize

resize是当n大于capacity时进行扩容,小于capacity时更新_finish的位置,我们看代码

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

五.push_back

5.1库中的push_back

我们使用时直接调用例如我们创建了一个vector<int> v,对象,我们想要插入1,我们直接调用v.push_back(1);

5.2模拟实现push_back

void push_back(const T& val)
{
	if (_finish == _endofstorage)
	{
		reserve(capacity() == 0 ? 4 : capacity() * 2);
	}
	*_finish = val;
	_finish++;
}

        和库中的一样,我们的const T&val中的T就是value_type,我们使用const的主要原因就是为了支持隐式类型转换,例如我们向要插入一个string类型我们插入方式就可以有下面几种

vector<string> v;
string s("abcde");
v.push_back(s);
v.push_back("aaaaa");

这个第二种就是单参数的隐式类型转换, 它转换成了一个临时对象,由于临时对象具有常性所以加const。

六.迭代器

6.1库中迭代器

这两个函数都是具有非const版本和const版本,那它们是如何实现的呢?我们看,模拟

6.2模拟

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

非const调用非const版本的,const调用const版本的,有了迭代器我们就可以进行变量,我们看下面代码

template <class T>
void print_vector(const vector<T>&v)
{
	for (int i = 0; i < v.size(); i++)
	{
		cout << v[i] << " ";
	}
	cout << endl;
	typename vector<T>::const_iterator it = v.begin();
    //auto it = v.begin();
	while (it != v.end())
	{
		cout << *it << " ";
		it++;
	}
	cout << endl;
	for (auto& x : v)
	{
		cout << x << " ";
	}
	cout << endl;

}

七.insert以及push_back的复用

7.1库中的insert

在这里面有三种,我们只模拟一种,给出插入位置,然后进行插入 

7.2模拟实现inser以及迭代器失效问题

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
		pos = _start + len;
	}

	iterator it = _finish - 1;
	while (it >= pos)
	{
		*(it + 1) = *it;
		--it;
	}
	*pos = val;
	++_finish;
}

        当我们扩容后,由于pos位置没有变化,就会导致迭代器失效,这时候我们需要更新pos的位置才可ui继续使用迭代器,所以我们在扩容器那先保存pos和_start的距离,更新后再更新pos的位置,然后进行插入。

7.3push_back复用insert

        有了迭代器,我们可以利用insert来实现我们的push_back,代码如下:

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

push_back就是在尾部进行插入,我们将我们的尾部传进行就可以进行插入。

八.删除

8.1pop_back

8.1.1库中的pop_back

这里就是直接将最后一个数据删除。

8.1.2模拟实现pop_back

void pop_back()
{
	assert(!empty());
	--_finish;
}
bool empty()
{
	return _start == _finish;
}

 在这里我们需要判断是否为空,不为空就进行删除,删除只需要我们直接挪动_finish指针就可以。

8.2erase

8.2.1库中的erase

erase就是删除某位置到某位置的的内容,至于返回值是iterator什维利应对迭代器失效问题。

8.2.1模拟实现erase以及迭代器失效问题

在这里我们只模拟实现删除pos位置的内容,代码如下:

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

我们直接挪动数据,然后将_finish进行左挪动,由于erase会造成和迭代器失效,例如我们删除一些数据中的偶数,

vector<int> v1;
v1.push_back(1);
v1.push_back(2);
v1.push_back(3);
v1.push_back(4);
v1.push_back(4); 
v1.push_back(4);
v1.push_back(5);
v1.push_back(6);
auto it = v1.begin();
while (it != v1.end())
{
	cout << *it << " ";
	it++;
}
cout << endl;

it = v1.begin();
while (it != v1.end())
{
	if (*it % 2 == 0)
	{
		v1.erase(it);
	}
	else
	{
		it++;
	}
}
cout << endl;
it = v1.begin();
while (it != v1.end())
{
	cout << *it << " ";
	it++;
}
cout << endl;

   在非扩容的版本中这个代码可以很好的实现,但是在缩容的版本中会造成it位置的改变,造成迭代器失效,所以库中返回值为iterator,当我们每次删除后都让it重新指向它,更新it的指向,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;
}

九.拷贝构造

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

        我们先开辟空间,由于我们可能需要进行深拷贝,所以我们直接进行迭代赋值就可以实现我们的拷贝构造。

十.再次分析默认构造

在库中支持先开辟n个空间,里面都是val,也可以支持迭代器进行构造,我们先看代码

template <class InputIterator>
vector(InputIterator begin, InputIterator end)
{
	while (begin != end)
	{
		push_back(*begin);
		begin++;
	}
}
vector(size_t n, const T& val = T())
{
	reserve(n);
	for (int 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);
	}
}

由于可以是很多类型的迭代器进行构造,我们可以写成模板,进行构造,对于第二种,我们可以先开n个空间,然后都赋值为val,由于val可能需要深拷贝,所以我们用push_back。对于第三种,由于我们传的两个参数都是int,我们向用第二种,但是第二种会有类型转换,第一种只有一种参数,很符合第一种,所以会选择第一种进行实例化,所以会造成出现问题,我们单独写一种就可以解决这个问题。

十一.赋值构造

vector<T>& operator=(vector<T> val)
{
	swap(val);
	return *this;	
}

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

我们传一个参数,不使用引用,让他进行拷贝构造,然后进行swap就可以实现我们的赋值构造

十二.析构

~vector()
{
	delete[] _start;
	_start = _finish = _endofstorage=nullptr;
}

直接将空间释放,就可以了。

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

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

相关文章

Qt的qtmqtt库连接onenet出现QMQTT::SocketRemoteHostClosedError解决方法

问题描述 在Qt发开过程中使用qtmqtt库来连接onenet的mqtt服务器&#xff0c;在ClientId、Username和Password均填写正确的情况下还是连接不上&#xff0c;查看错误显示QMQTT::SocketRemoteHostClosedError。 解决方法 client中的CleanSession标志位必须设置为true。 client …

ssh爆破模拟攻击(最基础)

攻击机&#xff08;192.168.83.136&#xff09; 靶机&#xff08;192.168.83.140&#xff09; 靶机打开22端口服务 centos7开启ssh 22端口_centos7开放22端口-CSDN博客 看这个师傅的操作 此实验对ssh端口漏洞利用的最基础实验 1、信息收集 发现22端口打开&#xff0c;尝…

k8s RBAC 角色访问控制详解与生产中的实际应用案例

&#x1f407;明明跟你说过&#xff1a;个人主页 &#x1f3c5;个人专栏&#xff1a;《Kubernetes航线图&#xff1a;从船长到K8s掌舵者》 &#x1f3c5; &#x1f516;行路有良友&#xff0c;便是天堂&#x1f516; 目录 一、前言 1、k8s简介 2、RBAC简介 二、RBAC关键…

Python_AI库 Pandas的数据结构及基本操作

Python_AI库 Pandas的数据结构及基本操作 本文默认读者具备以下技能&#xff1a; 熟悉python基础知识&#xff0c;vscode或其它编辑工具 熟悉表格文件的基本操作 具备自主扩展学习能力 本文篇幅较长&#xff0c;但比较重要&#xff0c;希望读者可以认真看完并实例运行。 Pa…

python在pyqt5+logging+threading模块实时显示日志

pyqt5logging模块&#xff0c;在界面上实时显示日志 简介代码实现logging.Handler重写界面创建与日志实时打印执行main.py 简介 在写小工具时&#xff0c;使用pyqt5生成界面&#xff0c;在代码中使用logging模块来打印信息&#xff0c;现在希望能将logging打印的信息实时展示在…

Android 开发工具使用

c调试 在NDK调试的时候&#xff0c;如果找不到 符号的话&#xff0c;我们可以在调试配置中添加符号地址的全路径一直到根目录&#xff1a;&#xff0c;xxx/armeabi-v7a&#xff1a; You must point the symbol search paths at the obj/local/ directory. This is also not a …

每日OJ题_DFS回溯剪枝⑦_力扣77. 组合

目录 力扣77. 组合 解析代码 力扣77. 组合 77. 组合 难度 中等 给定两个整数 n 和 k&#xff0c;返回范围 [1, n] 中所有可能的 k 个数的组合。 你可以按 任何顺序 返回答案。 示例 1&#xff1a; 输入&#xff1a;n 4, k 2 输出&#xff1a; [[2,4],[3,4],[2,3],[1,…

路透社:美国SEC将拒绝以太坊ETF

4月25日&#xff0c;据路透社报道&#xff0c;美国SEC在下个月将拒绝以太坊现货ETF申请。根据4位知情人士表示&#xff0c;在最近几周与美国证券交易委员会&#xff08;SEC&#xff09;进行了会议之后&#xff0c;美国发行商和其他公司预计SEC将拒绝他们推出与以太坊价格挂钩的…

windows pytorch安装

安装环境 WindowsAnacondaCudacuDNN Linux和Windows操作系统的安装存在差异&#xff0c;步骤会有所不同&#xff0c;本教程主要针对Windows系统进行示例。 Anaconda集成了许多方便的包和工具&#xff0c;使用会更加方便&#xff0c;特别适合科学计算&#xff0c;深度学习的数…

基于MATLAB野外观测站生态气象数据处理分析

朱老师(副教授)&#xff1a;来自国内重点高校&#xff0c;长期从事野外观测站生态气象监测与评估研究&#xff0c;发表SCl论文多篇&#xff0c;主持国家与地方科研项目多个,在生态环境数据处理与分析中具有丰富的实践项目经验。 以野外观测站高频时序生态气象数据为例&#xff…

ssm项目后端如何导出war及前端如何导出静态资源

后端如何导出war包 后端工具&#xff1a;IDEA 2020.1.3 运行我们编写工具maven里面的package 运行成功的日志 我们运行完&#xff0c;会生成一个target文件夹&#xff0c;在这个文件夹里面找到war包即可 前端如何导出静态资源 使用工具&#xff1a;WebStorm 2020.1.3 打开左…

c++的学习之路:28、哈希表

摘要 本章主要是说一下哈希的实现 目录 摘要 一、哈希表 1、哈希概念 2、闭散列 1、节点创建 2、插入 3、查找 4、删除 5、测试 3、开散列 1、创建 2、插入 3、查找 4、删除 5、析构函数 二、map 三、set 四、位图与布隆过滤器 五、代码 test.cpp HashT…

[移动端] “viewport“ content=“width=device-width, initial-scale=1.0“ 什么意思

布局视口, 代码如下 <!DOCTYPE html> <html lang"en"><head><meta charset"UTF-8"><title>Document</title><style>body,html {margin: 0;padding: 0;}.box {width: 200px;height: 200px;background-color: pi…

第五十周:文献阅读+IGRA-ISSA-LSTM

目录 摘要 Abstract 文献阅读&#xff1a;基于IGRA-ISSA-LSTM模型的水质预测 现有问题 提出方法 方法论 灰色关联分析&#xff08;GRA) 改进的灰色关联分析&#xff08;IGRA&#xff09; 麻雀搜索算法&#xff08;SSA&#xff09; 改进的麻雀搜索算法&#xff08;ISS…

【Redis】Redis安装、配置、卸载使用可视化工具连接Redis

文章目录 1.前置条件2.安装Redis2.1下载Redis安装包并解压2.2在redis目录下执行make命令2.3修改Redis配置文件2.4启动Redis服务2.5连接redis服务 3.Redis卸载4.使用可视化工具连接Redis 1.前置条件 Linux操作系统需要要是64位.如果不清楚自己Linux上是多少位的,可以使用以下命…

【机器学习原理】决策树从原理到实践

基于树的模型是机器学习中非常重要的一类模型&#xff0c;最基础的就是决策树&#xff0c;本篇主要讲述决策树的原理和几类最常见的决策树算法&#xff0c;这也是更复杂的树模型算法的基础。 参考文章&#xff1a; 1.CSDN-基于熵的两个模型(ID3,C4.5)比较详细&#xff0c;有数字…

JAVA SWING JTABLE表格,点击表头数据可以排序,且第一二行位置固定,不参与排序

对于JAVA SWING 界面开发&#xff0c;使用表格JTABLE开发过程中&#xff0c;一些情况下可能需要在点击表头时对数据进行排序处理。对于简单的排序处理&#xff0c;jtable的setAutoCreateRowSorter方法可满足&#xff0c;但是对于高要求的排序&#xff0c;则满足不了。 比如&am…

【leetcode】数组和相关题目总结

1. 两数之和 直接利用hashmap存储值和对于索引&#xff0c;利用target-nums[i]去哈希表里找对应数值。返回下标。 class Solution { public:vector<int> twoSum(vector<int>& nums, int target) {unordered_map<int, int> mp;vector<int> res;fo…

base64练习

找到password 搜索台搜索password 找到关于password关于base64算法的代码打上断点 点到这个位置在会有一个地址弹出来点击进去 缩小复制 粘贴 运行出数据&#xff0c;成功完成分析

Linux---为什么会有粘滞位?

在前面已经讲过目录的rwx权限&#xff1a; 可读权限(r): 如果目录没有可读权限, 则无法用ls等命令查看目录中的文件内容. 有可写权限(w):如果目录没有可写权限&#xff0c;则无法在目录中创建文件, 也无法在目录中删除文件.可执行权限(x): 如果目录没有可执行权限, 则无法cd到…