C++:STL:vector类常用函数介绍(附加部分重要函数模拟实现)

news2025/1/10 20:20:22

cplusplus.com/reference/vector/vector/icon-default.png?t=O83Ahttps://cplusplus.com/reference/vector/vector/

vector在实际中非常的重要,在实际中我们熟悉常见的接口就可以,有了string的基础,vector其实大体使用方法上二者是类似的:

这里我们先给出我们简单模拟实现时所用的成员函数:

private:
	iterator _start;
	iterator _finish;
	iterator _end_of_storage;

下文在介绍与string实现方式基本相同的地方,或者用法与string中相同函数基本一致的地方,均会直接以表格加实现代码的方式进行介绍,以便于我们重点介绍vector中我们需要了解的两个新知识:迭代器失效和更深层次的深浅拷贝问题。 

一,vector的定义

1.1(constructor)构造函数声明

cplusplus.com/reference/vector/vector/vector/icon-default.png?t=O83Ahttps://cplusplus.com/reference/vector/vector/vector/其中我们需要重点掌握的是以下几个构造函数:

以下是我们的简单实现代码:

template<class T>
class vector
{
public:	
	    vector()
			:_start(nullptr)
			,_finish(nullptr)
			,_end_of_storage(nullptr)
		{}

		template<class InputIterator>
		vector(InputIterator first, InputIterator last)
			:_start(nullptr)
			,_finish(nullptr)
			,_end_of_storage(nullptr)
		{
			assert(first && last);
			InputIterator itor = first;
			for (; itor < last; itor++)
			{
				push_back(*itor);
			}
		}
		vector(size_t n, const T& value = T())
			:_start(nullptr)
			, _finish(nullptr)
			, _end_of_storage(nullptr)
		{
			resize(n, value);
		}

		vector(const vector<T>& v)
			:_start(nullptr)
			,_finish(nullptr)
			,_end_of_storage(nullptr)
		{
			reserve(v.capacity());
			_finish = _start + v.size();
			for (size_t i = 0; i < size(); i++)
			{
				(*this)[i] = v[i];
			}
		}

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

其中的swap,push_back,resize以及reserve与string中的功能基本一样,我们后面在来介绍实现它们, 这里我们先不管它们如何实现。我们要重点说的是这里拷贝构造时遇到的深浅拷贝问题,这里先埋个伏笔。

1.2vector iterator 的使用

cplusplus.com/reference/vector/vector/begin/

cplusplus.com/reference/vector/vector/end/

 cplusplus.com/reference/vector/vector/rbegin/

 cplusplus.com/reference/vector/vector/rend/

typedef T* iterator;
typedef const T* const_iterator;
//iterator
iterator begin()
{
	return _start;
}

iterator end()
{
	return _finish;
}

const_iterator begin()const
{
	return _start;
}

const_iterator end()const
{
	return _finish;
}

二,vector 空间增长问题 

cplusplus.com/reference/vector/vector/size/ 

cplusplus.com/reference/vector/vector/capacity/

cplusplus.com/reference/vector/vector/empty/

cplusplus.com/reference/vector/vector/resize/

cplusplus.com/reference/vector/vector/reserve/

 

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

size_t capacity()const
{
	return _end_of_storage - _start;
}
void resize(size_t n, const T& pos = T())
{
	while (size() < n)
	{
		push_back(pos);
	}
	if (n <= size())
	_finish = _start + n;
}

接下来的reserve我们需要单独拎出来讲,先来看下我们在实现string的reserve时的模拟实现代码:

void string::reserve(size_t n)
{
	if (n > _capacity)
	{
		char* tmp = new char[n + 1] {'\0'};
		strcpy(tmp, _str);
		delete[] _str;
		_str = tmp;
		_capacity = n;
	}
}

从string的模拟实现我们第一时间会想到去用memcpy来完成对n>capacity时的数据拷贝。当然,这种方法对于我们所有的内置类型都没有问题,比如vector<int>,vector<char>等等, 但如果是vector<string>这种成员元素是标准库类型的数据呢, 如果我们只是简单的去拷贝每一个字节,其实是对每一个元素的浅拷贝,就像这样:

void reserve(size_t n)
{
	if (n > capacity())
	{
		size_t oldsize = size();
		T* tmp = new T[n];
		if (_start)
		{
			memcpy(tmp, _start, oldsize * sizeof(T));
			delete[] _start;
		}
		_start = tmp;
		_finish = _start + oldsize;
		_end_of_storage = _start + n;
	}
}

很明显的问题,接下来tmp中的string对象都会指向一块随即空间,也就是它们中的成员指针全部成了野指针。直接就会报错,解决方法其实也很简单,我们只需要改用for循环去拷贝数据即可,虽然会些许浪费时间,但是是我们简单模拟时一种较好的解决方法:

void reserve(size_t n)
{
if (n > capacity())
{
	size_t oldsize = size();
	T* tmp = new T[n];

	if (_start)
	{
		for (size_t i = 0; i < oldsize; i++)
		{
			tmp[i] = _start[i];
		}
		delete[] _start;
	}
	_start = tmp;
	_finish = _start + oldsize;
	_end_of_storage = _start + n;
	}
}

 除此之外,需要注意的点还有:

1.capacity的代码在vs和g++下分别运行会发现,vs下capacity是按1.5倍增长的,g++是按2倍增长的。这个问题经常会考察,不要固化的认为,vector增容都是2倍,具体增长多少是根据具体的需求定义的。vs是PJ版本STL,g++是SGI版本STL。
2.reserve只负责开辟空间,如果确定知道需要用多少空间,reserve可以缓解vector增容的代
价缺陷问题。
3.resize在开空间的同时还会进行初始化,影响size。

三,vector 增删查改 

 cplusplus.com/reference/vector/vector/reserve/

cplusplus.com/reference/vector/vector/push_back/

 cplusplus.com/reference/vector/vector/pop_back/

cplusplus.com/reference/algorithm/find/?kw=find

cplusplus.com/reference/vector/vector/insert/

cplusplus.com/reference/vector/vector/erase/

cplusplus.com/reference/vector/vector/swap/

 cplusplus.com/reference/vector/vector/operator[]/

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

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

//modify
void push_back(T n)
{
	if (_finish == _end_of_storage)
	{
		reserve(capacity() == 0 ? 4 : capacity() * 2);
	}
	*_finish = n;
	_finish++;	
}

void pop_back()
{
	assert(size());
	_finish--;
}

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

 

TIPS:迭代器失效问题

注意:Linux下,g++编译器对迭代器失效的检测并不是非常严格,处理也没有vs下极端。

这里我们借助insert与erase来介绍:

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

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

这里给出insert与erase主要是为了便于我们去对照vs与linux下实现代码的差异,下面我们来看第一种失效:

1.扩容引起的野指针:

我们来看下面一组例子:

void test_vector3()
{
	ELY::vector<int> v1;//ELY是我实现的vector所在的命名空间
	v1.push_back(1);
	v1.push_back(2);
	v1.push_back(3);
	v1.push_back(4);
	//v1.push_back(4);

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

	int x;
	cin >> x;
	auto it = find(v1.begin(), v1.end(), x);
	if (it != v1.end())
	{
		v1.insert(it, 10 * x);

		cout << *it << endl;
	}

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

这串代码在vs下的运行结果如下图:

显然此时我们的it已经失效,因为扩容后,it如果没有更新,此时it指向的空间已经被释放了,导致it迭代器成了野指针,致使迭代器失效。

2.删除数据导致数据的挪动致使迭代器失效

void test_vector4()
	{
		std::vector<int> v1;
		v1.push_back(1);
		v1.push_back(2);
		v1.push_back(3);
		v1.push_back(4);

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

		int x;
		cin >> x;
		auto it = find(v1.begin(), v1.end(), x);
		if (it != v1.end())
		{
			v1.erase(it);

			cout << *it << endl;
		}
    }

 vs下面会直接报错:

g++ 下面正常运行:

再来看这样一组例子,删除数据中的所有偶数:

void test_vector4()
{
	std::vector<int> v1;
	v1.push_back(1);
	v1.push_back(2);
    v1.push_back(2);
	v1.push_back(3);
	v1.push_back(4);

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

	auto it = v1.begin();
	while (it != v1.end())
	{
		if (*it % 2 == 0)
			v1.erase(it);
		++it;
	}
	for (auto i : v1)
	{
		cout << i << ' ';
	}
	cout << endl;
}

vs下面还是直接报错:

 

g++上面运行:

这里直接端错误。

换一组数据我们会发现偶数并没有被删干净。

///

从上述三个例子中可以看到:SGI STL中,迭代器失效后,代码并不一定会崩溃,但是运行
结果肯定不对,如果it不在begin和end范围内,肯定会崩溃的。

所以我们在使用迭代器的时候,一定要注意迭代器失效的问题,要及时更新我们的迭代器。

迭代器失效解决办法:在使用前,对迭代器重新赋值即可。

 

 

 

 

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

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

相关文章

MLCC贴片电容不同材质区别:【及电容工作原理】

贴片电容的材质常规有&#xff1a;NPO&#xff08;COG&#xff09;&#xff0c;X7R&#xff0c;X5R&#xff0c;Y5V 等&#xff0c;主要区别是它们的填充介质不同。在相同的体积下由于填充介质不同所组成的电容器的容量就不同&#xff0c;随之带来的电容器的介质损耗、容量稳定…

垂直领域的大模型应该如何构建?RAG还是微调呢?

垂直领域的大模型应该如何构建&#xff1f;RAG还是微调呢&#xff1f; 垂直领域的大模型应该是2024年乃至未来五年内人工智能发展的热门所在。那么该如何构建&#xff1f;是RAG&#xff08;Retrieval Augmentation Generation&#xff0c;检索增强生成&#xff09;还是微调&am…

Springboot 整合 Java DL4J 实现医学影像诊断功能

&#x1f9d1; 博主简介&#xff1a;历代文学网&#xff08;PC端可以访问&#xff1a;https://literature.sinhy.com/#/literature?__c1000&#xff0c;移动端可微信小程序搜索“历代文学”&#xff09;总架构师&#xff0c;15年工作经验&#xff0c;精通Java编程&#xff0c;…

PDF 转 CAD 工具:实现文档格式高效转换的利器

音频创作已经变得越来越普及啦&#xff0c;这就导致我们需要一款音乐软件来进行音频创作。作为刚入门的新手我比较推荐使用免费的音频剪辑软件来开启音乐之门。所以今天我们就一同来探讨有什么好用的音频剪辑工具吧。

【在Linux世界中追寻伟大的One Piece】信号捕捉|阻塞信号

目录 1 -> 信号捕捉初识 2 -> 阻塞信号 2.1 -> 信号其他相关常见概念 2.2 -> 在内核中的表示 2.3 -> sigset_t 2.4 -> 信号集操作函数 2.5 -> sigprocmask 2.6 -> sigpending 3 -> 捕捉信号 3.1 -> 内核如何实现信号的捕捉 3.2 ->…

以JavaScript的学习角度看Axios,并以spring boot+vue3为例具体分析实现

什么是Axios Axios 是一个基于 Promise 的 HTTP 客户端&#xff0c;用于在浏览器和 后端 中发送异步的 HTTP 请求。它功能强大、易用&#xff0c;常用于与 API 交互&#xff0c;发送 GET、POST、PUT、DELETE 等请求。 Axios 的主要特点&#xff1a; 支持 Promise Axios 基于 …

【数据结构笔记】搜索树

二叉搜索树 任一节点x的左/右子树中&#xff0c;所有非空节点均不大于&#xff08;不小于&#xff09;x 必须是所有的非空节点&#xff0c;仅左右孩子不够&#xff08;左孩子的右孩子可能很大&#xff09;一棵二叉树是二叉搜索树当且仅当中序遍历序列是单调非降序列 两棵二叉…

在电脑上免费压缩视频的 16 个视频压缩软件

正在寻找适用于 Windows 或 Mac 的最佳视频压缩器&#xff0c;让您轻松压缩 MP4、AVI、MKV、MOV 和更多类型的文件&#xff1f;无论您是通过社交媒体与朋友分享视频录制、释放手机空间&#xff0c;还是通过邮件发送长 MP4 视频&#xff0c;都必须使用付费或免费视频压缩软件来压…

2013年国赛高教杯数学建模D题公共自行车服务系统解题全过程文档及程序

2013年国赛高教杯数学建模 D题 公共自行车服务系统 公共自行车作为一种低碳、环保、节能、健康的出行方式&#xff0c;正在全国许多城市迅速推广与普及。在公共自行车服务系统中&#xff0c;自行车租赁的站点位置及各站点自行车锁桩和自行车数量的配置&#xff0c;对系统的运行…

提升邮件营销设计精准度秘诀,效率与效果实践

邮件营销通过确定目标群体、数据分析、邮件设计、测试优化、保持频率时效性及结合其他渠道实现精准营销&#xff0c;提高市场效益。ZohoCampaigns集成CRM、自动化功能和客户细分提升效果。 1、确定目标群体 精准营销的第一步是了解并确定你的目标群体。标定目标群体包括年龄、…

SpringSecirity(四)——用户退出

因为JWT是无状态的&#xff0c;去中心化的&#xff0c;在服务器端无法清除&#xff0c;服务器一旦进行颁发&#xff0c;就只能等待自动过期 才会失效&#xff0c;所以需要redis配合才能完成登录状态的记录。 实现思路&#xff1a; 登录后在redis中添加一个白名单&#xff0c;把…

信息系统运维管理方案,运维建设文档,运维平台建设方案,软件硬件中间件运维方案,信息安全管理(原件word,PPT,excel)

建设方案目录&#xff1a; 1、智慧运维系统建设背景 2、智慧运维系统建设目标 3、智慧运维系统建设内容 4、智慧运维系统建设技术 5、智慧运维系统建设流程 6、智慧运维系统建设收益 企业对运维管理的需求&#xff1a; 1、提高运维效率&#xff1a;降低运维成本&#xff0c;提高…

威纶通触摸屏与三菱FX5u之间 EtherNet/IP无线以太网通信方案

在实际系统中&#xff0c;同一个车间里分布多台PLC&#xff0c;由触摸屏集中控制。通常所有设备距离在几十米到上百米不等。在有通讯需求的时候&#xff0c;如果布线的话&#xff0c;工程量较大且不美观&#xff0c;这种情况下比较适合采用无线通信方式。本方案以威纶通触摸屏和…

Python数据分析-Scipy科学计算法

1.认识Scipy SciPy&#xff08;发音为 "Sigh Pie"&#xff09;是一个开源的 Python 算法库和数学工具包。 通常与 NumPy、Matplotlib 和 pandas 等库一起使用&#xff0c;这些库共同构成了 Python 的科学计算基础。 2.使用Scipy基本函数 2.1 引用Scipy函数 impor…

vue+echarts实现雷达图及刻度标注

文章目录 前言代码实现实现效果总结 前言 最近项目有做数据可视化 大屏 不免再次使用些echarts应用 记录下其中echarts雷达图的实现 代码实现 先上代码 <template><div class"container"><div ref"chart" style"width: 500px; heig…

【Spring AI】Java实现类似langchain的向量数据库RAG_原理与具体实践

介绍一下RAG&#xff1a; 检索增强生成&#xff08;RAG&#xff09;是一种技术&#xff0c;它结合了检索模型和生成模型来提高文本生成的质量。通过从企业私有或专有的数据源中检索相关信息&#xff0c;并将这些信息与大型语言模型相结合&#xff0c;RAG能够显著减少模型产生幻…

如何建立高质量的谷歌外链?

想做谷歌seo&#xff0c;外链是绝对绕不开的一个门槛&#xff0c;外链对网站的流量和SEO表现有很大帮助&#xff0c;正常途径想建立高质量外链需要策略和技巧&#xff0c;内容平台和博客是获取外链的好地方。在这些平台上发布文章并嵌入外链&#xff0c;不仅可以展示你的专业能…

删除链表的倒数第 N 个结点 | LeetCode-19 | 双指针 | 递归 | 栈 | 四种方法

&#x1f64b;大家好&#xff01;我是毛毛张! &#x1f308;个人首页&#xff1a; 神马都会亿点点的毛毛张 这道题还可以用递归法&#xff0c;你想到了吗&#xff1f;毛毛张介绍四种方法 LeetCode链接&#xff1a;19. 删除链表的倒数第 N 个结点 1.题目描述 给你一个链表&a…

《Linux从小白到高手》综合应用篇:深入理解Linux进程调优

本篇深入介绍Linux进程调优. 1. Linux系统进程类型&#xff1a; Linux的进程可能有成千上万个&#xff1a; ‌新建状态‌&#xff1a;进程刚刚被创建&#xff0c;但尚未运行。 ‌就绪状态‌&#xff1a;进程已经准备好运行&#xff0c;等待CPU分配。 ‌运行状态‌&#xff1…

解读 AI 获客关键要素,开启营销新未来

​在当今数字化浪潮席卷的时代&#xff0c;企业获客的难度与日俱增&#xff0c;传统方式逐渐力不从心。而 AI 获客宛如一颗璀璨的新星&#xff0c;为企业带来全新的机遇。 AI 获客凭借人工智能强大的数据分析能力&#xff0c;能从海量数据中精准挖掘出目标客户。其优势显著&…