C++ : STL容器之vector剖析

news2024/11/25 7:03:05

在这里插入图片描述

STL容器之vector剖析

  • 一、构造函数与赋值
    • (一)默认构造
    • (二)拷贝构造
    • (三)几个相同值构造
    • (四)迭代器构造
    • (五)initializer_list 构造
    • (六)赋值重载
  • 二、几个重要接口
    • (一)reserve
    • (二)resize
    • (三)erase
    • (四)insert
  • 三、vector 的实现(完整源码)
  • 四、结束语

一、构造函数与赋值

(一)默认构造

采用初始化列表初始化,将三个迭代器都初始化为 nullptr,用三个指针的关系间接地控制size,capacity以及``data`.

vector()
	: _start(nullptr)
	, _finish(nullptr)
	, _endofstorage(nullptr)
{}

(二)拷贝构造

拷贝构造可以用两种方式来实现,下面第一种是直接深拷贝的思路,另外还可以采用复用的方式。

这种方式可以使得拷贝后,size(), capacity()和拷贝的值相等

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

这种方式采用复用,简洁了代码,可以多采用这种方式书写代码。

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

(三)几个相同值构造

拷贝几个相同的值进入一个新的 vector,我们用了两个方式来实现,用 size_t来做参数,扩大n的取值范围,这样的化还需要重载一个函数 int,这是因为下面还有一个迭代器构造函数用函数模板实现,如果不这样的化,我们如果需要3个5的vector,就会自动调用函数模板,达不到预期,还会报错。

vector(size_t n, const T& value = T())
{
	_start = new T[n];
	for (int i = 0; i < n; i++) {
		_start[i] = value;
	}
	_finish = _start + n;
	_endofstorage = _start + n;
}

vector(int n, const T& value = T())
{
	_start = new T[n];
	for (int i = 0; i < n; i++) {
		_start[i] = value;
	}
	_finish = _start + n;
	_endofstorage = _start + n;
}

(四)迭代器构造

成员函数也可以用函数模板来实现,可以用其它容器的迭代器来构造 vector,只要能够进行自动类型转换即可。

		template<class InputIterator>
		vector(InputIterator first, InputIterator second)
		{
			size_t n = second - first;
			_start = new T[n];
			for (int i = 0; i < n; i++) {
				_start[i] = *(first + i);
			}
			_finish = _start + n;
			_endofstorage = _start + n;
		}
void test09()
	{
		vector<int> v1;
		v1.push_back(1);
		v1.push_back(2);
		v1.push_back(3);
		v1.push_back(4);

		vector<int> v2(v1.begin(), v1.end());
		for (auto e : v2)
		{
			cout << e << " ";
		}
		cout << endl;

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

		vector<int> v4(10, 1);
		vector<double> v5(10, 1.1);
		for (auto e : v4)
		{
			cout << e << " ";
		}
		cout << endl;
	}

在这里插入图片描述

(五)initializer_list 构造

initailzer_list是一个 std 库里面的一个对象,原理大概就是开一个数组用两个首尾指针来维护它

		vector(initializer_list<T> il)
			: _start(nullptr)
			, _finish(nullptr)
			, _endofstorage(nullptr)
		{
			int n = il.size();
			_start = new T[n];
			auto x = il.begin();
			for (int i = 0; i < n; i++) {
				_start[i] = *(x + i);
			}
			_finish = _start + n;
			_endofstorage = _finish;
		}

下面的实例中,v1先构造了一个initializer_list 对象,接着隐式类型转换,但是编译器优化为直接构造。而v2就是直接构造,接着下面演示了如何初始化这种对象。

void test11()
{
	vector<int> v1 = { 1,2,3,4,5,6 };
	vector<int> v2({ 1,2,3,4,5,6 });

	auto il1 = { 1, 2, 3, 4, 5, 6 };
	initializer_list<int> il2 = { 1, 2, 3, 4, 5, 6 };
	for (auto x : il2) {
		cout << x  << " ";
	}
}

(六)赋值重载

现代写法,这种写法简直拉满了,妙哉。
我们知道函数值传参会调用拷贝构造,我们只需要将需要赋值的对象和形参交换,就能达到赋值的效果,而函数结束后会调用析构函数自动释放形参的空间,妙不可言。

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

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

二、几个重要接口

(一)reserve

在完成这个函数时,一定要采取for循环来依次赋值,如果使用 memcpy ,会发生一个深层次深浅拷贝的问题,如果_start指向的这片空间设计到动态内存开辟,那么会造成析构时释放两次的现象

		void reserve(size_t n)
		{
			if (n > capacity()) {
				T* temp = new T[n];
				size_t cnt = size();
				if (_start) {
					for (size_t i = 0; i < cnt; i++) {
						temp[i] = _start[i];
					}
					delete[]_start;
				}
				_start = temp;
				_finish = _start + cnt;
				_endofstorage = _start + n;
			}
		}

(二)resize

设置有效元素个数,分情况讨论即可,如果 reserve 里面采用了memcpy 涉及到复用,这里也可能会报错。

void resize(size_t n, T value = T())
{
	if (n <= size()) {
		int num = size() - n;
		_finish -= num;
	}
	else {
		reserve(n);
	    while (size() < capacity()) {
			push_back(value);
		}
	}
}

(三)erase

删除指定元素,并且返回当前删除元素的下一个元素的迭代器,返回值需要注意,否则可能发生迭代器失效的问题。

		iterator erase(iterator pos)
		{
			assert(pos < _finish);
			assert(pos >= _start);
			for (auto x = pos + 1; x != _finish; x++) {
				*(x - 1) = *(x);
			}
			_finish--;
			return pos;
		}

(四)insert

插入一个元素,这里用到了 reserve,因此pos指向的元素可能已经是另外开辟的空间,造成迭代器失效,这种情况我们要记住pos_start的相对位置,这样我们才能在新的数组空间还原pos的位置。`

		iterator insert(iterator pos, const T& value)
		{
			assert(pos <= _finish);
			assert(pos >= _start);
			if (_finish == _endofstorage) {
				int n = pos - _start;
				reserve(capacity() == 0 ? 4 : 2 * capacity());
				pos = _start + n;
			}
			for (auto x = _finish; x > pos; x--) {
				*(x) = *(x - 1);
			}
			*(pos) = value;
			_finish++;
			return pos;
		}

三、vector 的实现(完整源码)

实现vector的时候,我们没有用 capacity size data 来定义,而是用了三个指针来间接地维护这几个值。

#define _CRT_SECURE_NO_WARNINGS 1

#include<iostream>
#include<vector>
#include<assert.h>
using namespace std;

namespace wgm {
	template<class T>
	class vector {
	public:
		typedef T* iterator;
		typedef const T* const_iterator;

		vector()
			: _start(nullptr)
			, _finish(nullptr)
			, _endofstorage(nullptr)
		{
			cout << "vector() 调用" << endl;
		}

		vector(initializer_list<T> il)
			: _start(nullptr)
			, _finish(nullptr)
			, _endofstorage(nullptr)
		{
			cout << "vector(initializer_list<T> il)调用" << endl;
			int n = il.size();
			_start = new T[n];
			auto x = il.begin();
			for (int i = 0; i < n; i++) {
				_start[i] = *(x + i);
			}
			_finish = _start + n;
			_endofstorage = _finish;
		}

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

		template<class InputIterator>
		vector(InputIterator first, InputIterator second)
		{
			size_t n = second - first;
			_start = new T[n];
			for (int i = 0; i < n; i++) {
				_start[i] = *(first + i);
			}
			_finish = _start + n;
			_endofstorage = _start + n;
		}

		vector(size_t n, const T& value = T())
		{
			_start = new T[n];
			for (int i = 0; i < n; i++) {
				_start[i] = value;
			}
			_finish = _start + n;
			_endofstorage = _start + n;
		}

		vector(int n, const T& value = T())
		{
			_start = new T[n];
			for (int i = 0; i < n; i++) {
				_start[i] = value;
			}
			_finish = _start + n;
			_endofstorage = _start + n;
		}

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

		T& operator[](size_t n) 
		{
			assert(n < size());
			assert(n >= 0);
			return _start[n];
		}

		const T& operator[](size_t n) const
		{
			assert(n < size());
			assert(n >= 0);
			return _start[n];
		}

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

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

		iterator begin()
		{
			return _start;
		}

		iterator end()
		{
			return _finish;
		}

		const_iterator begin() const
		{
			return _start;
		}

		const_iterator end() const
		{
			return _finish;
		}

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

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

		void reserve(size_t n)
		{
			if (n > capacity()) {
				T* temp = new T[n];
				size_t cnt = size();
				if (_start) {
					for (size_t i = 0; i < cnt; i++) {
						temp[i] = _start[i];
					}
					delete[]_start;
				}
				_start = temp;
				_finish = _start + cnt;
				_endofstorage = _start + n;
			}
		}

		void resize(size_t n, T value = T())
		{
			if (n <= size()) {
				int num = size() - n;
				_finish -= num;
			}
			else {
				reserve(n);
			    while (size() < capacity()) {
					push_back(value);
				}
			}
		}

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

		bool empty() {
			return _start == _finish;
		}

		void pop_back()
		{
			assert(!empty());
			_finish--;
		}

		iterator insert(iterator pos, const T& value)
		{
			assert(pos <= _finish);
			assert(pos >= _start);
			if (_finish == _endofstorage) {
				int n = pos - _start;
				reserve(capacity() == 0 ? 4 : 2 * capacity());
				pos = _start + n;
			}
			for (auto x = _finish; x > pos; x--) {
				*(x) = *(x - 1);
			}
			*(pos) = value;
			_finish++;
			return pos;
		}

		iterator erase(iterator pos)
		{
			assert(pos < _finish);
			assert(pos >= _start);
			for (auto x = pos + 1; x != _finish; x++) {
				*(x - 1) = *(x);
			}
			_finish--;
			return pos;
		}
		

	private:
		iterator _start;
		iterator _finish;
		iterator _endofstorage;
	};
}

四、结束语

相比于stringvector新加入的一些新的元素,但是大同小异,后面的学习中研究不同点即可,vector中的迭代器失效,以及深浅拷贝的问题在学习时需要注意。

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

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

相关文章

网络编程(19)——C++使用asio协程实现并发服务器

十九、day19 上一节学习了如果通过asio协程实现一个简单的并发服务器demo&#xff08;官方案例&#xff09;&#xff0c;今天学习如何通过asio协程搭建一个比较完整的并发服务器。 主要实现了AsioIOServicePool线程池、逻辑层LogicSystem、粘包处理、接收协程、发送队列、网络…

C语言入门:打开编程世界的大门

一.C语言是什么 在我们生活中&#xff0c;我们在交流时候使用的就是语言&#xff0c;在这个世界上有许多的国家、民族&#xff0c;自然也有很多语言如&#xff1a;汉语、英语、法语等等&#xff0c;这种人与人交流使用的语言我们称为自然语言。然而计算机并不能理解我们的语言…

github下载文件的两种方式(非git形式)

1.以下面的图为例 &#xff0c;可以直接点击右上方的绿色Code按键&#xff0c;在弹出的列表中选择Download Zip选项&#xff0c;即可下载。 2.如果下载的是单独的某一个文件&#xff0c;则可以按照下图的格式点击下图所示的那个下载的图标即可。

【Linux系统编程】第三十一弹---深入理解静态库:从零开始制作与高效使用的完全指南

✨个人主页&#xff1a; 熬夜学编程的小林 &#x1f497;系列专栏&#xff1a; 【C语言详解】 【数据结构详解】【C详解】【Linux系统编程】 目录 1、静态库 1.1、怎么做静态库 1.2、怎么使用静态库 1、静态库 1.1、怎么做静态库 在Linux环境下&#xff0c;通常使用GCC&am…

【2024最新】基于springboot+vue的实验室管理系统lw+ppt

作者&#xff1a;计算机搬砖家 开发技术&#xff1a;SpringBoot、php、Python、小程序、SSM、Vue、MySQL、JSP、ElementUI等&#xff0c;“文末源码”。 专栏推荐&#xff1a;SpringBoot项目源码、Vue项目源码、SSM项目源码、微信小程序源码 精品专栏&#xff1a;Java精选实战项…

PAT甲级-1034 Head of a Gang

题目 题目大意 一个犯罪团伙满足条件&#xff1a;人数 > 2&#xff1b;团伙内的总通话时长 > k。团伙首领就是该团伙中通话时长最多的。先给定一组通话&#xff0c;格式为 A B time&#xff0c;要求输出犯罪团伙的数目&#xff0c;并输出每个团伙的首领名字和该团伙的人…

一文详解数据库范式

背景 在开发中&#xff0c;我们经常需要考虑如何设计合适的表结构&#xff0c;而则往往需要考虑数据库的范式。数据库的三范式&#xff08;3NF&#xff09;是数据库设计过程中用来减少数据冗余和提高数据一致性的重要规则。它们分别是第一范式&#xff08;1NF&#xff09;、第二…

【PR小技巧】PR技术分享 一 PR关键帧小技巧介绍

在Adobe Premiere Pro (简称PR) 中&#xff0c;关键帧是用于控制视频剪辑、音频轨道、效果动画等随时间变化的重要工具。通过合理使用关键帧&#xff0c;可以实现各种复杂的动画效果和精确的时间控制。今天我们就来学习一些关于关键帧的小技巧&#xff0c;以及具体的例子来说明…

算法专题五: 位运算

目录 常见位运算总结1. 位1的个数2. 比特位计数3. 汉明距离4. 只出现一次的数字5. 只出现一次的数字Ⅲ6. 判定字符是否唯一7. 丢失的数字8. 两正数之和9. 只出现一次的数字Ⅲ10. 消失的两个数字 常见位运算总结 重点 : 1. 位1的个数 算法思路: 这道题就用到了我们总结的那个第…

全新YOLOv11美化版检测界面 涵盖超多功能 支持百种模型改进训练

文章目录 前言视频效果必要环境一、界面功能概述1. 运行方法2. 图像选择图像:表格信息:统计信息:IOU和NMS调节:目标框显示: 3. 文件夹选择文件夹:进度显示:推理结果: 4. 视频、摄像头进度显示:实时检测:帧状态回溯: 5. 替换界面中的模型5. 鼠标悬浮 二、训练改进模型运行方法假…

力扣周赛:第419场周赛

&#x1f468;‍&#x1f393;作者简介&#xff1a;爱好技术和算法的研究生 &#x1f30c;上期文章&#xff1a;力扣周赛&#xff1a;第415场周赛 &#x1f4da;订阅专栏&#xff1a;力扣周赛 希望文章对你们有所帮助 因为一些特殊原因&#xff0c;这场比赛就打了1h&#xff0c…

[Linux] Linux 模拟实现 Shell

标题&#xff1a;[Linux] Linux 模拟实现 Shell 个人主页水墨不写bug&#xff08;图片来源于网络&#xff09; 目录 一、什么是shell 二、shell的理解 三、模拟实现shell 1&#xff09;打印命令行提示 2&#xff09;获取用户的输入字符串 3&#xff09;分割命令字符串 4…

【优选算法篇】双指针的优雅舞步:C++ 算法世界的浪漫探索

文章目录 C 双指针详解&#xff1a;基础题解与思维分析前言第一章&#xff1a;对撞指针1.1 移动零解题思路图解分析C代码实现易错点提示代码解读 1.2 复写零解题思路算法步骤C代码实现易错点提示代码复杂度 1.3 盛最多水的容器1. 题目链接2. 题目描述解法一&#xff08;暴力求解…

链表(4)_合并K个升序链表_面试题

个人主页&#xff1a;C忠实粉丝 欢迎 点赞&#x1f44d; 收藏✨ 留言✉ 加关注&#x1f493;本文由 C忠实粉丝 原创 链表(4)_合并K个升序链表_面试题 收录于专栏【经典算法练习】 本专栏旨在分享学习算法的一点学习笔记&#xff0c;欢迎大家在评论区交流讨论&#x1f48c; 目录…

第十五届蓝桥杯C++B组省赛

文章目录 1.握手问题解题思路1&#xff08;组合数学&#xff09;解题思路2&#xff08;暴力枚举&#xff09; 2.小球反弹做题思路 3.好数算法思路&#xff08;暴力解法&#xff09;---不会超时 4.R格式算法思路 5.宝石组合算法思路---唯一分解定理 6.数字接龙算法思路----DFS 7…

【Oracle数据库进阶】001.SQL基础查询_查询语句

课 程 推 荐我 的 个 人 主 页&#xff1a;&#x1f449;&#x1f449; 失心疯的个人主页 &#x1f448;&#x1f448;入 门 教 程 推 荐 &#xff1a;&#x1f449;&#x1f449; Python零基础入门教程合集 &#x1f448;&#x1f448;虚 拟 环 境 搭 建 &#xff1a;&#x1…

Egg考古系列-EggCore的生命周期

关于EGG egg框架的第一个版本还是2017-03-21&#xff0c;距今已有7年了。虽然最近几年没有什么更新&#xff0c;但它在国内的使用还是挺多的&#xff0c;mvc的分层模式也很受大家喜欢。虽然声称是面向企业级、中大型项目场景的框架&#xff0c;但这种约定式在大型项目中其实也很…

高校学科竞赛管理:SpringBoot实现的高效策略

2相关技术 2.1 MYSQL数据库 MySQL是一个真正的多用户、多线程SQL数据库服务器。 是基于SQL的客户/服务器模式的关系数据库管理系统&#xff0c;它的有点有有功能强大、使用简单、管理方便、安全可靠性高、运行速度快、多线程、跨平台性、完全网络化、稳定性等&#xff0c;非常…

【M2-Mixer】核心方法解读

abstract&#xff1a; 在本文中&#xff0c;我们提出了M2-Mixer&#xff0c;这是一种基于MLPMixer的结构&#xff0c;具有多头损失&#xff0c;用于多模态分类。它比基于卷积、循环或神经结构搜索的基线模型具有更好的性能&#xff0c;其主要优势是概念和计算简单。所提出的多…

【电子电力】LCL滤波器设计,包括电流控制调谐

摘要 LCL 滤波器是电力电子领域中广泛应用于并网逆变器的滤波器之一&#xff0c;其主要功能是减少高频开关的谐波&#xff0c;确保输出电流的质量。本文设计并实现了基于 MATLAB 的 LCL 滤波器模型&#xff0c;结合电流控制器和调谐技术&#xff0c;验证了其在谐波抑制方面的效…