探索C++中的动态数组:实现自己的Vector容器

这篇具有很好参考价值的文章主要介绍了探索C++中的动态数组:实现自己的Vector容器。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

探索C++中的动态数组:实现自己的Vector容器,c++的学习之路,c++,开发语言,c语言,数据结构,vector,STL,容器

🎉个人名片

🐼作者简介:一名乐于分享在学习道路上收获的大二在校生
🙈个人主页🎉:GOTXX
🐼个人WeChat:ILXOXVJE
🐼本文由GOTXX原创,首发CSDN🎉🎉🎉
🐵系列专栏:零基础学习C语言----- 数据结构的学习之路----C++的学习之路
🐓每日一句:如果没有特别幸运,那就请特别努力!🎉🎉🎉
————————————————

🎉文章简介

🎉本篇文章将 介绍如何使用C++编写代码来实现一个类似于STL中的Vector容器 等学习的相关知识进行分享!

💕如果您觉得文章不错,期待你的一键三连哦,你的鼓励是我创作动力的源泉,让我们一起加 油,一起奔跑,让我们顶峰相见!!!🎉🎉🎉
——————————————————————————

一.前言

这篇文章将介绍如何使用C++编写代码来实现一个类似于STL中的Vector容器。Vector是一种动态数组,它可以根据需要自动调整大小,并提供了许多方便的方法来操作数据。在这篇文章中,你将学习如何使用指针和动态内存分配来创建一个可变大小的数组,并实现Vector的常见功能,如添加元素、删除元素、访问元素等。通过实现自己的Vector容器,你将更好地理解动态数组的原理和实现方式,并提升对C++语言的理解和掌握。

二.Vs下Vector的底层结构

vs下底层是一个类,类里面的成员变量包括三个指针,指针类型为所存储数据类型(T)的指针;
T* _start 指向的是存储数据所开空间的起始位置;
T* _finish 指向的是最后一个数据的下一个位置;
T* _endofstorage 指向的是所开空间的最后的下一个位置;

如图:
探索C++中的动态数组:实现自己的Vector容器,c++的学习之路,c++,开发语言,c语言,数据结构,vector,STL,容器

public:
	typedef T* iterator;
	typedef const T* const_iterator;
private:
	iterator _start;
	iterator _finish;
	iterator _endofstorage;

三.vector的模拟实现

1.构造函数

1.直接初始化为空指针,使用时再开空间

vector()
	:_start(nullptr)
	,_finish(nullptr)         //也可以在定义的时候直接给缺省值
	,_endofstorage(nullptr)
{}

2.用一个迭代器区间构造(需要复用下面实现的push_back函数)

	template<class intputiterator>     //模板
	vector(intputiterator first, intputiterator last)
	{
		while (first != last)  //不能是用<判断,因为底层不一定连续
		{
			push_back(*first);    //依次取里面的数据尾插
			++first;
		}
	}

3.用n个T类型构造对象(这里需要后面实现的resize函数)

vector(size_t n,const T& x = T())
{
	resize(n,x);
}
vector(int n, const T& x = T())
{
	resize(n,x);
}

注意:这里为什么要实现两个?
探索C++中的动态数组:实现自己的Vector容器,c++的学习之路,c++,开发语言,c语言,数据结构,vector,STL,容器

2.reserve函数

结合下面代码和图解看看思路解析:
1.reserve函数可以单独使用,也可以在其他接口种会使用,我们实现的该函数不会缩容,所以最开始会加一个判断是否缩容;
2.reserve函数的实现是开一个新空间,将原空间的数据拷贝到新空间,再对_start,_finish,_endofstorage 进行处理;
3.这里需要记录一个原空间存储的有效数据的个数,为了确定_finish的位置(如果不存储,则当开辟好了新空间后,_finish的位置不能确定)
图解:
探索C++中的动态数组:实现自己的Vector容器,c++的学习之路,c++,开发语言,c语言,数据结构,vector,STL,容器

void reserve(size_t n)
{
	if (n > capacity())
	{
		size_t old_size = size();      //旧空间有效数据个数
		size_t newcapacity = capacity() == 0 ? 4 : 2 * capacity();   //需要判断,因为可能为0
		iterator tmp = new T[newcapacity];     //开空间
		//memcpy(tmp, _start,old_size * sizeof(T));   //下面会将为什么不用memmove函数
		for (int i = 0; i < old_size; i++)
		{
			tmp[i] = _start[i];       //拷贝数据
		}
		delete[] _start;             //释放旧空间
		_start = tmp;
		_finish = _start + old_size;      //确定_finish的位置
		_endofstorage = _start + newcapacity;     
	}
}

为什么不用memmove?
当我们存储的数据类型为string时【如图一】每个string对象里面都有一个_str,指向一个字符串,当我们用memmove拷贝后,拷贝后的_str与拷贝前的_str指向同一块空间【如图二】,当我们释放_start的时候,会调用string的析构函数,将该空间释放掉,就会导致野指针问题;

探索C++中的动态数组:实现自己的Vector容器,c++的学习之路,c++,开发语言,c语言,数据结构,vector,STL,容器

3.push_back函数

思路:检查是否空间满了,扩容,直接尾插

	void push_back(const T& x)
	{
		if (_finish == _endofstorage)    //检查是否需要扩容
		{
			reserve(capacity() == 0 ? 4 : 2 * capacity());
		}

		*_finish = x;     //尾插

		++_finish;    //更新下标
	}

4.push_back函数

思路:与顺序表实现一样,直接–_finish;

void pop_back()
{
	assert(size());   //检查是否还有有效数据可删
	--_finish;
}

5.resize函数

思路:
分为三种可能:
1.n>capacity 扩容+尾插
2.size<n<capacity 直接尾插
3.n<size 尾删

值得注意的是传参给了缺省值,因为存储数据可能是string这种数据,给了缺省值会去掉对应的默认构造函数,虽然内置类型没有默认构造,但是为了解决这类问题,有了类似于默认构造类处理内置类型;


		void resize(size_t n,T x=T() )   
		{
			if (n<size())     //直接改变_finish
			{
				_finish = _start + n;;
			}
			else
			{
				if (n > capacity())   //扩容
				{
					reserve(n);
				}
				for (size_t i = size(); i <= n; i++)  //尾插
				{
					_start[i] = x;      //可以复用push_back函数
				}
				_finish = _start + n;    //更新
			}
			
		}

6.insert函数

思路:
1.检查是否需要扩容
2.挪动数据
3.插入,更新下标

iterator insert(iterator pos, const T& x)
{
	assert(pos);
	assert(pos >= _start);     //断言
	assert(pos <= _finish);
	if (_finish == _endofstorage)
	{
		size_t len = pos - _start;              //记录之前的值
		reserve(capacity() == 0 ? 4 : 2 * capacity();    //扩容
		pos = _start + len;          //更新pos下标
	}
	//memmove(pos+1, pos, sizeof(T) * (_finish - pos));  
	iterator end = _finish-1;
	while (end>=pos)
	{
		*(end+1) = *end;     //拷贝
		end--;
	}
	*pos = x;     //插入
	++_finish;    

	return pos;    //返回pos位置的迭代器,防止迭代器失效问题
}

7.erase函数

直接挪动数据覆盖

	iterator erase(iterator pos)
	{
		assert(pos < _finish && pos >= _start);   //断言

		iterator end = pos;
		while (end < _finish)
		{
			*end = *(end + 1);   //挪动数据覆盖
			end++;
		}
		--_finish;          //更新下标

		return pos;      //返回pos位置的迭代器,防止迭代器失效问题
	}

8.swap函数


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

9.赋值运算符重载

与上章《魔法之线:探索string类的神秘世界》链接: link赋值运算符重载方法一样;

现代写法:

	vector<T>& operator=(vector<T> v)
	{
		swap(v);      

		return *this;
	}

10.拷贝构造函数

传统写法:

	vector(const vector<T>& v)
	{
		iterator tmp = new T[v.capacity()];
		memcpy(tmp, v._start, sizeof(T) * v.size());
		_start = tmp;
		_finish = _start + v.size();
		_endofstorage = _start + v.capacity();

	}

稍便捷的方式
直接复用尾插函数,将对象v的数据一个一个尾插;

		vector(vector<T>& v)
			:_start(nullptr)
			,_finish(nullptr)
			,_endofstorage(nullptr)
		{
			reserve(v.capacity());      //提前空间,减少尾插扩容
			for (const auto& e : v)
			{
				push_back(e);
			}
		}

11.其他简单函数的实现:

		//判空
		bool empty()
		{
			return size();
		}
		//返回第一个数据
		T& front()const
		{
			return *_start;
		}
		//返回最后一个数据
		T& back()const
		{
			return *(_finish-1);
		}
		//返回有效数据个数
		size_t size()const
		{
			return _finish - _start;
		}
		//返回容量
		size_t capacity()const
		{
			return _endofstorage - _start;
		}
		//[]运算符重载
		T& operator[](size_t pos)
		{
			assert(pos>=0 && pos<size());

			return _start[pos];
		}

		T& operator[](size_t pos)const
		{
			assert(pos >= 0 && pos < size());

			return _start[pos];
		}
		iterator begin()
		{
			return _start;
		}

		iterator end()
		{
			return _finish;
		}

		const_iterator begin()const
		{
			return _start;
		}

		const_iterator end()const
		{
			return _finish;
		}

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

💕最后希望内容对大家有所帮助😊😊😊

探索C++中的动态数组:实现自己的Vector容器,c++的学习之路,c++,开发语言,c语言,数据结构,vector,STL,容器文章来源地址https://www.toymoban.com/news/detail-840444.html

到了这里,关于探索C++中的动态数组:实现自己的Vector容器的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 【C++】vector容器的模拟实现

    目录 一,框架设计 二,构造函数 三,析构函数 四,赋值运算符 五,容器接口的实现 1,迭代器实现 2,“ [] ”运算符的实现 3,swap交换和resize重设大小 4,insert插入和erase删除 介绍:         本文,我们重点实现vector容器的用法,这里要注意的是vector容器可以接纳任意类

    2024年02月02日
    浏览(45)
  • 【C++杂货铺】探索vector的底层实现

    STL(standard template libaray-标准模板库): 是C++标准库的一部分 ,不仅是一个可复用的组件库,而且 是一个包罗数据结构与算法的软件框架 。 原始版本 :Alexander Stepanov、Meng Lee在惠普实验室完成的版本,本着开源精神,它们声明允许任何人任意运用、拷贝、修改、传播、商业使

    2024年02月11日
    浏览(29)
  • C++ —— STL容器【vector】模拟实现

    本章代码gitee仓库:vector模拟实现、vector源码 看源码发现 vector 是类模板定义的,成员是采用迭代器进行管理 当涉及到容器类时,通常有一些关键函数,如构造函数、析构函数和拷贝构造函数,它们负责初始化容器对象、销毁对象和进行对象的拷贝等 这里注意拷贝构造要实现

    2024年02月16日
    浏览(32)
  • C++ stl容器vector的底层模拟实现

    目录 前言:   1.成员变量,容量与大小 2.构造函数 无参构造: 带参的使用值进行构造:  使用迭代器区间进行构造: 3.交换 4.拷贝构造 5.赋值重载 6.迭代器 7.扩容 reserve: resize: 8.插入与删除 insert: erase: insert迭代器失效问题: erase迭代器失效问题: 9.头插头删 10.[]重载

    2024年04月15日
    浏览(31)
  • 【C++庖丁解牛】vector容器的简易模拟实现(C++实现)(最后附源码)

    🍁你好,我是 RO-BERRY 📗 致力于C、C++、数据结构、TCP/IP、数据库等等一系列知识 🎄感谢你的陪伴与支持 ,故事既有了开头,就要画上一个完美的句号,让我们一起加油 我们前面介绍了vector容器的概念以及对其基本使用进行了介绍,如果你在这里不知道vector是什么以及不知

    2024年03月14日
    浏览(34)
  • 【C++】容器篇(一)—— vector 的基本概述以及模拟实现

    前言: 在之前,我们已经对 string类进行了基本的概述,并且手动的实现了string类中常用的接口函数。本期,我将带领大家学习的是STL库中的一个容器 -- vector 的学习。相比于之前的string类,本期的 vector 相对来说实现起来略微难一点,难点就在于要考虑关于 “ 迭代器失效 ”

    2024年02月07日
    浏览(34)
  • 【C++】:STL源码剖析之vector类容器的底层模拟实现

    构造一个空vector size和capacity为0 将_start _finish _endofstorage 都置为空指针即可 传统写法 : 1). 新开辟一块和 v 同样容量的空间,更新 _start, _finish, _endofstorage 2). 将 v 中的数据拷贝到新开辟的空间中 注意 : 不要使用memcpy函数拷贝数据,如果数据是内置类型或浅拷贝的自定义类型

    2024年02月04日
    浏览(40)
  • C++中的vector类模拟实现

    目录 vector模拟实现 vector类设计 vector类构造函数 vector类根据个数构造函数 vector类根据迭代器区间构造函数 vector类拷贝构造函数 vector类赋值运算符重载函数 vector类析构函数 vector类获取有效数据个数函数 vector类获取容量大小函数 vector类begin()函数 vector类end()函数 vector类reser

    2024年04月13日
    浏览(27)
  • 【C++】反向迭代器的模拟实现通用(可运用于vector,string,list等模拟容器)

    🌏博客主页: 主页 🔖系列专栏: C++ ❤️感谢大家点赞👍收藏⭐评论✍️ 😍期待与大家一起进步! 我们要写出一个通用的反向迭代器模拟而且在保证代码简介不繁琐的的情况下,一定程度上使用我们自己模拟的已经封装好的iterator迭代器可以简化许多步骤,首先我们要知

    2024年02月14日
    浏览(42)
  • 【C++】中的vector使用详解及重要部分底层实现

           本篇文章会对vector的语法使用进行详解。同时,还会对重要难点部分的底层实现进行讲解。其中有vector的 迭代器失效 和 深拷贝 问题。希望本篇文章的内容会对你有所帮助。 目录 一、vector 简单概述 1、1 C语言中数组的不便 1、2 C++中的动态数组容器vector  二、vector的

    2024年02月15日
    浏览(26)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包