C++vector 简单实现

news2025/7/30 10:58:34

一。概述

vector是我们经常用的一个容器,其本质是一个线性数组。通过对动态内存的管理,增删改查数据,达到方便使用的目的。
作为一个线性表,控制元素个数,容量,开始位置的指针分别是

start  /*是数组的首地址*/
finish /*是数组元素的终止位置,看下图*/
end_of_storage/*是数组元素的总容量,看下图*/

大概的样子:
在这里插入图片描述
我们通过操作此三个指针和内存的增加减少或者复制转移的方式完成vector的成员方法。
下面是类成员定义的展示:

template<class _Ty> #类模板中没 有内存分配器,我们在函数中自己malloc实现#
class MyVector
{
public:
	typedef _Ty				value_type;
	typedef _Ty* pointer;
	typedef const _Ty* const_pointer;
	typedef _Ty& reference;
	typedef const _Ty& const_reference;

	typedef pointer        iterator;
	typedef const_pointer  const_iterator;

	typedef unsigned  int  size_type;
private:
	_Ty* _M_start;
	_Ty* _M_finish;
	_Ty* _M_end_of_storage;


public:
	MyVector() :_M_start(nullptr), _M_finish(nullptr), _M_end_of_storage(nullptr) {}
	~MyVector() {
		clear();
		free(_M_start);
	}

reference at(size_type pos)
	{
		assert(pos >= 0 && pos < size());
		//return (*(pos + _M_start));
		return (_M_start[pos]);
	}
	const_reference at(size_type pos) const
	{
		assert(pos >= 0 && pos < size());
		return (*(pos + _M_start));
	}
	
	reference operator[](size_type pos)
	{
		if (pos >= 0 && pos < size()) {
			return _M_start[pos];
		}
		else {
			std::cout << "error operator[]";
			exit(1);
		}
	}
	const_reference operator[](size_type pos) const
	{
		if (pos >= 0 && pos < size()) {
			return _M_start[pos];
		}
		else {
			std::cout << "error operator[]";
			exit(1);
		}
	}

	reference front()
	{
		return *_M_start;
	}
	const_reference front() const
	{
		return *_M_start;
	}

	reference back()
	{

		return *(_M_finish -1);
	}
	const_reference back() const
	{
		return *(_M_finish - 1);
	}
	
	_Ty* data()
	{
		return begin(); 
	}
	const _Ty* data() const
	{
		return begin();
	}

public:
	// 容量
	size_t size() const
	{
		return (size_t)(_M_finish - _M_start);
	}
	size_t capacity() const
	{
		return (size_t)(_M_end_of_storage - _M_start);
	}
	/*2/14*/
	bool empty()const
	{
		return size() > 0 ? true : false;
	}

二。重点成员方法解析

1.push_back尾部添加元素
不管pushback进的值是内置类型还是类类型,都应该是先创建一个空间,在此基础上生成对象实例化。实例化是在构造函数中进行的。
我们也要知道的是,一个进程的地址空间是4G,给定的数据(只读或是读、写数据)是固定大小,一旦容器像临时数组arr[100]的定义,那么的成员所占的内存如果过大或是动态的增减的难度,那么势必会不方便容器的使用,也不方便管理,所以使用动态内存来进行管理。

void push_back(const _Ty& val)
	{
		if (_M_finish != _M_end_of_storage)
		{
			new(_M_finish)_Ty(val);
			++_M_finish;
		}
		else {
			
			_M_insert_aux(end(),val);
		}
	}

_M_insert_aux 函数实现

_M_insert_aux函数中有一部分是从源空间的元素复制到新位置,此时挨个遍历,原位构造的方式相比于连续空间的赋值构造,我猜测后者效率更高一些(从cpuj计算的角度)。

		void _M_insert_aux(iterator pos, const _Ty& val) //
		{
			//const size_t oldsize = size();
			const size_t len = size() != 0 ? (size() * 1.5 + 1) : 1;
			//iterator new_start = (_Ty*)malloc(sizeof(_Ty) * len);

			iterator  start2 = (_Ty*)malloc(sizeof(_Ty) * len);
			//eg1:开始复制数据到新内存
				iterator newstart = start2;
				iterator newfinsh = _M_start;
				/*while (newfinsh != pos)
				{
					new(newstart)_Ty(*newfinsh);
					++newstart;
					++newfinsh;
				}
				*/
				/*new(newstart)_Ty(val);
				++newstart;*/
				//eg1的另一种写法,测试:
				while (newfinsh != pos)
				{
					memcpy(start2, _M_start, sizeof(_Ty)*(_M_finish-_M_start));
					newstart = _M_finish - _M_start + start2;
					newfinsh = _M_finish;
				}
			
				new(newstart)_Ty(val);
				++newstart;
				iterator it = _M_finish;
				while (newfinsh != _M_end_of_storage)
				{
					++newfinsh;
					++newstart;
				}
				for (iterator it = _M_start; it != _M_finish; it++)
				{
					it->~_Ty();
				}
				free(_M_start);
		
			_M_finish = newstart;
			_M_end_of_storage = len + start2;
			_M_start = start2;
		}

**2.operator =移动赋值
次函数需要注意的是什么形式时编译器调用的是赋值重载,什么形式时会调用拷贝构造。
隐式的拷贝构造:在创建对象性时的赋值,比如;
MyVector<Int
> tmp= arr;
显示的拷贝构造则是:
MyVector<Int
> tmp(arr);
如果你使用vector时的写法是隐式的,那么会调用拷贝构造完成对像的复制;
相反你使用显示的方式创建对象,也是会调用拷贝构造。
当你在创建对象后的赋值运算符的重载时才会调用vector的移动赋值函数

MyVector& operator= (const MyVector& V1)//在赋值运算符重载中不能在调用拷贝构造
											//因为this已经是一个对象,再拷贝构造创建一个对象,
											//是没什么意义的事情。
	{//arr.operator(V1);
		if (V1._M_start == nullptr&&_M_start ==nullptr) {
			return *this;
		}
		else if (this->_M_start == nullptr) { //拷贝构造
			/*iterator start = (_Ty*)malloc(sizeof(_Ty) * V1.capacity());
			_M_start = start;*/
			const_iterator it = V1.begin();
			while (it != V1._M_finish) {
				this->push_back(*it);
				++it;
					
			}
			/*_M_finish = ( _Ty* )it;
			_M_end_of_storage = V1.capacity()+_M_start;*/
			return *this;
		}
		else if (this->_M_start != nullptr) {
			if (this->size() >= V1.size())
			{
				/*for (iterator it = _M_start; it != _M_finish; ++it)
				{
					it->~_Ty();
				}*/
				this->clear();
				iterator it = (_Ty*)V1.begin();
				while (it != V1._M_finish) {
					this->push_back(*it);
					++it;

				}
				return *this;
			}
			else {
				this->clear();
				free(_M_start);
				_M_start = nullptr;
				_M_finish = nullptr;
				this->_M_end_of_storage = nullptr;
			/*	iterator start = (_Ty*)malloc(sizeof(_Ty) * V1.capacity());
				_M_start = start;*/
				iterator it = (_Ty*)V1.begin();//iterator it =V1._M_start;
				while (it != V1._M_finish) {
					this->push_back(*it);
					++it;

				}
				/*_M_finish = it;
				_M_end_of_storage = V1.capacity() + _M_start;*/
				return *this;
			}
		}
	}

3.拷贝构造
注意:在类的成员方法内部不要在类中几个默认的成员函数(构造,析构,赋值重载,取地址运算符的重载)中调用拷贝构造,没有什么意义。从面向对象编程的角度讲不符合现实世界的逻辑。

MyVector(const MyVector& V1)
	{
		cout << this << endl;
			iterator start = (_Ty*)malloc(sizeof(_Ty) * V1.capacity());
			if (start == nullptr)
			{
				std::cout << "malloc error" << std::endl;
				exit(1);
			}
			iterator finish = start;
			iterator p = V1._M_start;
				_M_start = start;
				while (p != V1._M_finish)
				{
					new(finish)_Ty(*p);
					++p;
					++finish;
				}
				_M_finish = finish;
				_M_end_of_storage = _M_start + V1.capacity();
	}

4.erase

iterator erase(iterator _F, iterator _L)
	{
		if (_F != _L)
		{
			if (_L != _M_finish) 
				copy(_L, _M_finish, _F);
				_M_finish = _M_finish - (_L - _F);
				memset(_M_finish , 0x00, sizeof(_Ty) * (_L - _F));
			
		}
		return _L;
	}
	
	iterator erase(iterator pos)//有返回值,迭代器返回
	{

		return erase(pos, pos + 1);
		/*if (pos != end()) //这个代码可被erase(pos,pos+1)替代
		{
			copy(pos+1,_M_finish,pos);
			(--_M_finish)->~_Ty();
		}
		return pos;*/
	}

需要注意的是,上面使用到memcpy/memmove/copy函数时,不能保证对有虚函数的类(能产生多态的类)可以继续使用多态。原因是虚函数表和虚表指针丢失,无法找到函数指针(虚表指针应该不丢失)。

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

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

相关文章

Hive---拉链表

拉链表 文章目录拉链表定义用途案例全量流程增量流程合并过程第一步第二步第三步案例二&#xff08;含分区&#xff09;创建外部表orders增量分区表历史记录表定义 拉链表是一种数据模型&#xff0c;主要是针对数据仓库设计中表存储数据的方式而定义的&#xff0c;顾名思义&am…

从零开始学GeoServer源码十一(如何处理多个文件解析器Multipart Resolver引起的冲突问题)

目录前言1.现象2.排查问题3.找到问题4.解决问题5.总结前言 本文起源于我们遇到的一个问题&#xff0c;本来 GeoServer 使用的好好的&#xff0c;但是有天突然发现&#xff0c;无法在 GeoServer 中上传样式的 sld 文件了&#xff0c;报错 “No Multipart-config for Servlet” …

java.lang.IllegalArgumentException: itemView may not be null

报错截图&#xff1a;场景介绍&#xff1a;在使用recycleView 自动递增数据&#xff0c;且自动滚动到最新行&#xff1b; 当数据达到273条 时出现ANR&#xff1b;项目中 全部的列表适配器使用的三方库&#xff1a;BaseRecyclerViewAdapterHelper &#xff08;很早之前的项目&am…

《SQL基础》16. 锁

锁锁全局锁表级锁表锁元数据锁意向锁行级锁行锁间隙锁临键锁锁 锁是计算机协调多个进程或线程并发访问某一资源的机制。在数据库中&#xff0c;除传统的计算资源&#xff08;CPU、RAM、I/O&#xff09;的争用以外&#xff0c;数据也是一种供许多用户共享的资源。如何保证数据并…

uniapp在线升级关联云空间

升级中心 uni-upgrade-center - App&#xff1a; https://ext.dcloud.net.cn/plugin?id4542 App升级中心 uni-upgrade-center文档&#xff1a; https://uniapp.dcloud.net.cn/uniCloud/upgrade-center.html#uni-upgrade-center-app 升级中心 uni-upgrade-center - Admin&#…

Ka频段需要更多带宽?

随着全球连接需求的增长&#xff0c;许多卫星通信(satcom)系统日益采用Ka频段&#xff0c;对数据速率的要求也水涨船高。目前&#xff0c;高性能信号链已经能支持数千兆瞬时带宽&#xff0c;一个系统中可能有成百上千个收发器&#xff0c;超高吞吐量数据速率已经成为现实。 另…

JavaWeb—HTML

目录 1、B/S 软件的结构 2、前端的开发流程 3、网页的组成部分 4、HTML 简介 5、创建 HTML 文件 6、HTML 文件的书写规范 7、HTML 标签介绍 8、常用标签介绍 8.1、font 字体标签 8.2、特殊字符 8.3、标题标签 8.4、超链接 &#xff08; **** 重 点 &#xff0c;必 …

如何实现jwt鉴权机制之详解

jwt鉴权一是什么headerpayloadSignature二、如何实现生成 token校验token三、优缺点优点&#xff1a;缺点&#xff1a;一是什么 JWT&#xff08;JSON Web Token&#xff09;&#xff0c;本质就是一个字符串书写规范&#xff0c;如下图&#xff0c;作用是用来在用户和服务器之间…

Wannacrypt蠕虫老树开花?又见Wannacrypt

Wannacrypt蠕虫是一个在2017年就出现的远古毒株&#xff0c;其利用永恒之蓝漏洞降维打击用户服务器&#xff0c;而后进行扩散勒索&#xff0c;曾经一度风靡全球&#xff0c;可谓是闻者伤心&#xff0c;听着落泪&#xff0c;因为这玩意解密是不可能 解密的。 而2023年的今天&am…

MCM 箱模型建模方法及大气 O3 来源解析实用干货

OBM 箱模型可用于模拟光化学污染的发生、演变过程&#xff0c;研究臭氧的生成机制和进行敏感性分析&#xff0c;探讨前体物的排放对光化学污染的影响。箱模型通常由化学机理、物理过程、初始条件、输入和输出模块构成&#xff0c;化学机理是其核心部分。MCM (Master Chemical M…

【每天学习一点新知识】JNDI注入

什么是JNDIJNDI是Java的一种API&#xff0c;为我们提供了查找和访问各种命名和目录服务的通用统一的接口。通过JNDI统一接口我们可以来访问各种不同类型的服务&#xff0c;例如远程方法调用&#xff08;RMI&#xff09;&#xff0c;通用对象请求代理体系结构&#xff08;CORBA&…

Qt QTreeView简单使用

QT-QTreeView使用方法 QTreeView: 用于显示树状结构数据&#xff0c;适用于树状结构数据的操作。 一、初始化 ​ 利用QStandardlternModel来初始化数据&#xff0c;标准的基于项数据的数据模型类&#xff0c; 每个项数据可以是任何数据类型。 // 初始化model QStandardItem…

工作实战之拦截器模式

目录 前言 一、结构中包含的角色 二、拦截器使用 1.拦截器角色 a.自定义拦截器UserValidateInterceptor&#xff0c;UserUpdateInterceptor&#xff0c;UserEditNameInterceptor b.拦截器配置者UserInterceptorChainConfigure&#xff0c;任意组装拦截器顺序 c.拦截器管理者…

VM安装FydeOS16.0

准备工作 1、已安装VMware Workstation虚拟机&#xff1b; 2、下载好系统文件&#xff1b; 3、打开VM、新建虚拟机&#xff1b; 一、下载 https://fydeos.com/download/vm 我选择的镜像1。等下载完成&#xff0c;我这感觉下载速度不快&#xff0c;通过onedrive下载要快。 …

Jfrog 搭建本地maven仓库以及上传Android库

Jfrog 下载 安装包下载地址&#xff1a;Download Artifactory OSS | JFrog 如果是想下载之前的版本&#xff0c;可以点击上面的Get code source &#xff0c;如果是最新版本&#xff0c;直接点下面的下载就好。下面以Linux安装为例。 Jfrog安装 对于Linux而言&#xff0c;其实…

Java泛型深入

一. 泛型的概述和优势 泛型概述 泛型&#xff1a;是JDK5中引入的特性&#xff0c;可以在编译阶段约束操作的数据类型&#xff0c;并进行检查。泛型的格式&#xff1a;<数据类型>&#xff0c;注意&#xff1a;泛型只能支持引用数据类型。集合体系的全部接口和实现类都是…

Java刷题-----蓝桥杯省赛JavaC组第十二届(第二场)4-------------6

4、格点题目本题总分&#xff1a;10 分问题描述如果一个点 ( x , y ) 的两维坐标都是整数&#xff0c;即 x ∈ Z 且 y ∈ Z &#xff0c;则称这个点为一个格点。如果一个点 ( x , y ) 的两维坐标都是正数&#xff0c;即 x > 0 且 y > 0 &#xff0c;则称这个点在第一象限…

浅谈 Nodejs原型链污染

一直在做php的题目&#xff0c;对其它语言做的很少。刚好在西湖论剑2022复现时&#xff0c;遇到了一道原型链污染的题目&#xff0c;借此机会开始简单学习一下 Nodejs的洞 p&#x1f402;讲解的十分清楚&#xff0c;因此下面举例子就直接用p&#x1f402;的例子进行解释了 目…

SNMP学习和测试

学习 &#xff08;1&#xff09;SNMP是简单网络管理协议&#xff0c;但是多么晦涩我就不说了。 &#xff08;2&#xff09;SNMP工作在应用层&#xff0c;也就是通过socket实现的&#xff0c;基于UDP&#xff0c;端口161和162&#xff0c;161是用于和网管通信的端口&#xff0…

NYUv2生成边界GT(1)

看了cityscape和NYUv2生成边界GT的代码后&#xff0c;因为自己使用的是NYUv2数据集&#xff0c;所以需要对自己的数据集进行处理。CASENet生成边界GT所使用的代码是MATLAB&#xff0c;所以又重新看了一下MATLAB的代码&#xff0c;并进行修改&#xff0c;生成了自己的边界代码。…