首页 > 其他分享 >vector常见接口的实现

vector常见接口的实现

时间:2024-09-13 15:53:09浏览次数:11  
标签:finish const iterator 常见 接口 start vector size

前言

vector容器

vector是可以动态变化大小的数组容器。所以它的很多操做和数组类似,虽然如此,但它毕竟也是一个类,所以在是线上与数组还是有不小的区别。

本文章主要从底层去模拟实现vector的常见接口的实现,vector有多个接口,每个接口的功能都对应着不同的效果,常见功能有增删改查和扩容等。

vector的优点

vector可以动态去管理数据,它的本质上就是一个动态数组。

vector既然是一个数组他就能随机访问,支持排序。

访问数据元素比较高效。

vector缺点

vector在中间插入和删除已经头部插入的时候效率极低,这就需要后续用到的list填补这一缺点。

vector的定义

vector定义变量格式:vector<类型> 变量名。

比如:vector<int> v1,vector<char> v2,vector<string> v3,vector<float> v4等等。

由以上特性,所以我们底层模拟实现vector的时候就需要用模板的思想去实现。

由于vector支持迭代器的操作方法,所以我们需要把成员变量都设为迭代器类型。

vector类定义

template<typename T>
	class vector
	{
        public:
		typedef T* iterator;  //T*
        

        private:
        iterator _start;首个元素位置
        iterator _finish;//末尾元素的下一个位置
        iterator _endofstorage;//容量大小的下一个位置
    
    };

vector构造函数

主要目的是给vector进行初始化。

vector()
	:_start(nullptr), //初始化列表
	_finish(nullptr),
	_endofstorage(nullptr)
{

}

vector拷贝构造

vector(const vector<T>& v)
{
	_start = new T[capacity()];
	_finish = _start;
	_endofstorage = _start + v.capacity();
	for (size_t i = 0; i < v.size(); i++)
	{
		*_finish = v[i];
		_finish++;
	}
}

简便拷贝构造写法

vector(const vector<T>& v)
	:_start(nullptr),
	_finish(nullptr),
	_endofstorage(nullptr) //初始化
{
	reserve(v.capacity());//开辟空间
	for (const auto& e : v)
	{
		push_back(e);//尾插
	}
}

swap重载函数

目的是实现三个变量的交换实现深拷贝

void swap(vector<T> & v)
{
	::swap(_start, v._start); //::解决函数同名,调用的是全局的swap而非此处的swap
	::swap(_finish, v._finish);
	::swap(_endofstorage, v._endofstorage);
}

vector的获取长度和容量的函数

size()以及capacity()

由于vector定义的成员变量都为iterator类型,所以我们需要通过他们的差值获取size和capacity。

size()

int size()const  //加const是因为便于const定义的vector对象调用这个函数
{
	return _finish - _start;
}

capacity()

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

迭代器

迭代器由普通迭代器,常量迭代器,和反向迭代器(未实现)

iterator begin()
{
	return _start;
}
iterator end()
{
	return _finish;
}
iterator begin() const  //常量迭代器
{
	return _start;
}
iterator end() const 
{
	return _finish;
}

operator[]的实现

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

const T& operator[](size_t i) const //只能读且const定义的变量调用
{
	assert(i < size());
	return _start[i];
}

operator=的实现

vector<int>& operator=(vector<T>& v) //传参数应用,避免再次拷贝构造,返回应用是因为避免返回时候
{                                   //临时拷贝一个变量,设置返回值是因为要支持连等。
	swap(v); //直接交换三个变量地址
	return *this;
}

reserve()

reserve是实现扩容,但不会初始化。

void reserve(size_t ca)
{
	if (ca > capacity())
	{
		int sz = size();
		iterator temp = new T[ca];
		memcpy(temp, _start, sizeof(T) * sz);

		delete[] _start;
		_start = temp;
		_finish = temp + sz;
		_endofstorage = temp+ca;
	}
}

reserve的实现要注意:

在vector<>在内置类型下用memcpy没有问题,但如果在自定义类型下将会有重大隐患,比如我们定义一个vector<string>的时候。

比如上面这段代码的时候运行结果就会出现乱码

他就会出现代码,当我们继续push_back的时候程序就会直接挂掉。

这是什么原因呢,这就有关于memcpy的浅拷贝有关了,由于string的拷贝必须用深拷贝进行,否则就会导致新开辟string的对象指针的地址指向的与需要拷贝的源指针所指向的地址相同,然后将源地址delete之和,最后程序结束又会去调用新开辟string对象的析构函数,导致了一块地址被析构了两次,就导致程序崩溃。

所以,由于定义的是vector<string>的自定义类型,所以它也会存在深浅拷贝的问题,memcpy本质上就是浅拷贝,所以我们在这里的代码就需要修改为深拷贝。

void reserve(size_t ca)
{
	if (ca > capacity())
	{
		int sz = size();
		iterator temp = new T[ca];
		/*memcpy(temp, _start, sizeof(T) * sz);*/
		for (int i = 0; i < sz; i++) //赋值
		{
			temp[i] = _start[i];
		}

		delete[] _start;
		_start = temp;
		_finish = temp + sz;
		_endofstorage = temp+ca;
	}
}

resize()

调整空间大小,可用于开辟空间,开辟之后自动初始化。

void resize(size_t n, const T& val= T()) //T()表示缺省值类似于int()
{
	if (n >= capacity())
	{
		if(n>capacity()) reserve(n);
		for (_finish; _finish < _endofstorage; _finish++)
		{
			*_finish = val;
		}
	}
	else if (n < capacity())
	{
		_finish = _start + n;
	}
}

push_back()

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

pop_back()

尾删,只需要长度减少就行。

void pop_back()
		{
			assert(_finish > _start);
			--_finish;
		}

insert()

中间插入,支持尾插,头插。

void insert(iterator pos, const T& value)
{
	assert(pos <= _finish);
	size_t n = pos - _start;
	if (_finish == _endofstorage)
	{
		size_t newcapacity = (capacity() == 0 ? 2 : 2 * capacity());
		reserve(newcapacity);
		pos = n + _start; //坑,存在pos失效,由于扩容原因
	}

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

insert需要注意的点就是pos失效,在进行扩容之后,pos 是指向之前的数组的中间位置,而新开辟的空间不存在pos这时候就会导致pos失效,这时我们需要重新调整pos的位置。

erase()

iterator& erase(iterator pos)
{
	assert(pos >= _start);
	iterator it = pos;
	while (it < _finish)
	{
		*it = *(it + 1);
		it++;
	} 
	_finish--;
	return pos;
}
迭代器失效
  • 迭代器失效指的是迭代器失去了原本的作用,或者在某条需要使用迭代器的代码中迭代器出现了混乱情况,这些情况统称为迭代器失效。
  • 在vector的内部最容易出现迭代器失效的地方分别是 insert 和 erase

 情景一:insert扩容插入时

这就是由于扩容后,it还记录着原来位置的空间的位置,而扩容之后插入位置的地址变了,所以导致无法插入数据,迭代器失效。

情景二:erase删除的时候。

比如删除数组中的偶数

因为erase删除是覆盖原来,所以当偶数连续出现就会出现删不干净的情况。

所以准确代码应该修改为

析构函数

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

标签:finish,const,iterator,常见,接口,start,vector,size
From: https://blog.csdn.net/m0_63703622/article/details/142210397

相关文章

  • 常见的并非模型
    在并行编程中,处理循环迭代时常用的并发模型有几种:WorkerPool(工作池):描述:创建固定数量的工作goroutine,这些goroutine从共享的任务队列中获取任务并执行。优点:控制并发量,避免过多goroutine导致资源耗尽。示例:packagemainimport("fmt""sync")......
  • MySQL中常见的存储引擎有什么?
    MySQL中常见的存储引擎有什么?MySQL中有三种常见的引擎:InnoDB(默认),MyISAM,Memory。InnoDB存储引擎作为MySQL的默认存储引擎有很多特点:B+树作为索引结构,叶子节点上存放表中的数据,非叶子节点存放索引。支持事务ACID---->原子性,一致性,隔离性,持久性。事务隔离级别。(读未提交,读......
  • Java常见报错
    NoSuchElementException:一般都是数组或者集合的索引越界ConCurrentCheck(并发修改异常):因为集合中有自己的修改次数记录的变量,还有另一个记录地变量,一般这2个变量不一致,则会报错!mapkeyisrequired怎么解决:说明:MyBatis查询一些记录,数据涉及到两个表里的数据,需要连表查......
  • git常见问题Q&A
    git基本命令解释gitrestore--staged.:移除暂存区文件,不影响本地(撤销gitadd.操作)gitadd-u:将删除文件的操作同步到暂存区。将本地的删除同步到版本库(删除本地文件后执行,然后再gitpush)gitrm[-r]--cachedxxx:将文件或目录从git索引中删除,不影响本地文件。通常配......
  • WebM视频如何转为MP4格式?四步搞定常见视频转换
    WebM是一种开放的、免版税的多媒体容器格式,而MP4则更普遍地被各类设备支持。如果你有需要将WebM格式的视频转换为MP4格式,那么本教程简鹿办公将会指导你如何使用简鹿视频格式转换器完成这一任务。WebM转MP4步骤指南简鹿视频格式转换器Win在线包https://downloadopen......
  • 对接外卖霸王餐API接口,需要注意哪些事项?
    在对接外卖霸王餐API接口的过程中,需要注意以下事项以确保顺利实施和提供优质的用户体验:1.明确业务需求:确定你的平台需要哪些功能,如展示霸王餐选项、下单、支付、订单跟踪等。2.选择合适的API服务提供商:选择信誉良好、服务稳定的API服务提供商,并确保其提供的接口能满足你的......
  • DevExpress WPF中文教程:如何解决排序、过滤遇到的常见问题?(二)
    DevExpressWPF拥有120+个控件和库,将帮助您交付满足甚至超出企业需求的高性能业务应用程序。通过DevExpressWPF能创建有着强大互动功能的XAML基础应用程序,这些应用程序专注于当代客户的需求和构建未来新一代支持触摸的解决方案。无论是Office办公软件的衍伸产品,还是以数据为中心......
  • 电商API接口安全:构建稳固的数字防线
    电子商务的蓬勃发展带来了前所未有的便利,同时也带来了新的安全挑战。API接口作为电商系统的核心组件,其安全性直接关系到企业的数据安全和业务连续性。因此,评估和加固电商API接口的安全性变得尤为重要。电商API接口安全的重要性电商API接口安全不仅涉及到数据的保密性、完整性和......
  • PbootCMS网站内页打不开提示404的3种常见原因以及解决方法
    PbootCMS网站内页打不开提示404错误通常有几种常见的原因及解决方法:常见原因及解决方法环境配置错误原因:服务器环境配置不当,比如伪静态规则没有正确配置。解决方法:确认伪静态规则文件是否正确复制到了服务器上。通常伪静态规则文件是 .htaccess(Apache)或 nginx.conf(N......
  • 常见概念 -- WDM/OTN 时延
    什么是时延?        在通信网络中,时延指原始数据经一台转发设备的编码等一系列处理过程后由发送端发送,通过传输链路传输,到达另一台(目的地)设备的接收端并解码还原为原始数据所花费的时间。网络时延主要由以下几个部分组成:发送时延:原始数据进入转发设备开始到从发送端发......