vector的模拟实现

2023-11-18

vector的模拟实现

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

namespace myVector
{
	template<class T>
	class vector
	{
	public:
		// Vector的迭代器是一个原生指针
		typedef T* iterator;
		typedef const T* const_iterator;

		///
		// 构造和销毁
		vector()
			: _start(nullptr)
			, _finish(nullptr)
			, _endOfStorage(nullptr)
		{}

		vector(size_t n, const T& value = T())
			: _start(nullptr)
			, _finish(nullptr)
			, _endOfStorage(nullptr)
		{
			reserve(n);
			while (n--)
			{
				push_back(value);
			}
		}

		/*
		* 理论上讲,提供了vector(size_t n, const T& value = T())之后
		* vector(int n, const T& value = T())就不需要提供了,但是对于:
		* vector<int> v(10, 5);
		* 编译器在编译时,认为T已经被实例化为int,而10和5编译器会默认其为int类型
		* 就不会走vector(size_t n, const T& value = T())这个构造方法,
		* 最终选择的是:vector(InputIterator first, InputIterator last)
		* 因为编译器觉得区间构造两个参数类型一致,因此编译器就会将InputIterator实例化为int
		* 但是10和5根本不是一个区间,编译时就报错了
		* 故需要增加该构造方法
		*/
		vector(int n, const T& value = T())
			: _start(new T[n])
			, _finish(_start + n)
			, _endOfStorage(_finish)
		{
			for (int i = 0; i < n; ++i)
			{
				_start[i] = value;
			}
		}

		// 若使用iterator做迭代器,会导致初始化的迭代器区间[first,last)只能是vector的迭代器
		// 重新声明迭代器,迭代器区间[first,last)可以是任意容器的迭代器
		template<class InputIterator>
		vector(InputIterator first, InputIterator last)
		{
			while (first != last)
			{
				push_back(*first);
				++first;
			}
		}
		
		void swap(vector<T>& v)
		{
			std::swap(_start, v._start);
			std::swap(_finish, v._finish);
			std::swap(_endOfStorage, v._endOfStorage);
		}
		
		vector(const vector<T>& v)
			: _start(nullptr)
			, _finish(nullptr)
			, _endOfStorage(nullptr)
		{
			//现代写法,资本家写法
			vector<T> temp(v.begin(),v.end());
			swap(tmp);
			
		}

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

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

		/
		// 迭代器相关
		iterator begin()
		{
			return _start;
		}

		iterator end()
		{
			return _finish;
		}

		const_iterator cbegin() const
		{
			return _start;
		}

		const_iterator cend() const
		{
			return _finish;
		}

		//
		// 容量相关
		size_t size() const
		{
			return _finish - _start;
		}

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

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

		void reserve(size_t n)
		{
			if (n > capacity())
			{
				size_t oldSize = size();
				// 1. 开辟新空间
				T* tmp = new T[n];

				// 2. 拷贝元素
				// 这里直接使用memcpy会有问题吗?请思考下
				//if (_start)
				//	memcpy(tmp, _start, sizeof(T)*size);

				if (_start)
				{
					for (size_t i = 0; i < oldSize; ++i)
						tmp[i] = _start[i];

					// 3. 释放旧空间
					delete[] _start;
				}

				_start = tmp;
				_finish = _start + oldSize;
				_endOfStorage = _start + n;
			}
		}

		void resize(size_t n, const T& value = T())
		{
			// 1.如果n小于当前的size,则数据个数缩小到n
			if (n <= size())
			{
				_finish = _start + n;
				return;
			}

			// 2.空间不够则增容
			if (n > capacity())
				reserve(n);

			// 3.将size扩大到n
			iterator it = _finish;
			_finish = _start + n;
			while (it != _finish)
			{
				*it = value;
				++it;
			}
		}

		///
		// 元素访问
		T& operator[](size_t pos)
		{
			assert(pos < size());
			return _start[pos];
		}

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

		T& front()
		{
			return *_start;
		}

		const T& front()const
		{
			return *_start;
		}

		T& back()
		{
			return *(_finish - 1);
		}

		const T& back()const
		{
			return *(_finish - 1);
		}
		/
		// vector的修改操作
		iterator insert(iterator pos, const T& x)
		{
			assert(pos <= _finish);

			// 空间不够先进行增容
			if (_finish == _endOfStorage)
			{
				size_t n = pos - _start;
				size_t newCapacity = (0 == capacity()) ? 1 : capacity() * 2;
				reserve(newCapacity);

// 如果发生了增容,重新开辟空间后,reserve会更新start和finish,但是不会更新pos,原空间被释放掉,迭代器失效了,所以需要重置pos
				pos = _start + n;
			}

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

			*pos = x;
			++_finish;
			return pos;
		}

		// 返回删除数据的下一个数据
		// 方便解决:一边遍历一边删除的迭代器失效问题
		iterator erase(iterator pos)
		{
			// 挪动数据进行删除
			iterator begin = pos + 1;
			while (begin != _finish) {
				*(begin - 1) = *begin;
				++begin;
			}

			--_finish;
			return pos;
		}
		void push_back(const T& x)//防止深拷贝,尽量用引用传参
		{
			insert(end(), x);
		}

		void pop_back()
		{
			erase(end() - 1);
		}

		
		
	private:
		iterator _start;		// 指向数据块的开始
		iterator _finish;		// 指向最后有效数据的下一个位置
		iterator _endOfStorage;  // 指向存储容量的尾
	};
}

使用memcpy拷贝问题

假设模拟实现的vector中的reserve接口中,使用memcpy进行的拷贝,以下代码会发生什么问题?

int main()
{
	bite::vector<swx::string> v;
	v.push_back("1111");
	v.push_back("2222");
	v.push_back("3333");
	
	return 0;
}

问题分析:

  1. memcpy是逐字节拷贝,将一段内存空间中内容原封不动的拷贝到另外一段内存空间中
  2. 如果不涉及资源管理,memcpy既高效又不会出错,但如果涉及到资源管理时,就会出错,因为memcpy的拷贝实际是浅拷贝。

在这里插入图片描述
在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

如果对象中涉及到资源管理时,千万不能使用memcpy进行对象之间的拷贝,因为memcpy是浅拷贝,可能会引起一系列浅拷贝问题,所以我们要使用赋值运算符来完成,如果不涉及资源管理,那就正常赋值,如果涉及资源管理,那赋值运算符中也已经实现了深拷贝。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

vector的模拟实现 的相关文章

随机推荐

  • linux 解锁用户被锁

    一般我们在开发时部署好了环境 有时候用xshell 登录服务器时我们经常会忘记用户的登录密码 我们经常会遇到用户被锁定 首先 用root 用户查看 查看被锁用户的错误登录次数 pam tally2 u tom tom 为用户 pam tal
  • qt集成cef QWidget

    编译libcef dll wrapper 假设你已经编译出了libcef dll wrapper lib Debug和Release版本 并且对应版本的程序集类型分别是 MDd和MD qt的运行时库是MDd类型的 因此cef3编译的时候也应
  • hive 异常之 MetaException

    直接启动hive后 hive gt show databases FAILED SemanticException org apache hadoop hive ql metadata HiveException java lang Run
  • vue3中对本地存储的数据多次修改并实时页面显示

    背景 项目中遇到切换用户时 对App页面的信息进行实时显示 登录时存储一次 切换时再次存储 解决办法 1 在每次存储的同时存储到pinia中 可解决实时显示问题 import useCommonStore from pinia ls set
  • 软件测试中的43个功能测试点总结

    功能测试就是对产品的各功能进行验证 根据功能测试用例 逐项测试 检查产品是否达到用户要求的功能 针对web系统的常用测试方法如下 1 页面链接检查 每一个链接是否都有对应的页面 并且页面之间切换正确 可以使用一些工具 如LinkBotPro
  • C++学习(四八七)android studio println的输出位置

    程序中调用如下输出 System out println haha1 调试情况下 在Run和LogCat下均看不到输出 运行情况下 在Run下能看到输出 建议如下 可在LogCat中看到信息 android util Log常用的方法有以下
  • 蓝牙之十三-HFPclient JNI层

    JNI到app JAVA
  • 开营

    2021未来杯区块链应用创新大赛已于9月24日正式启动 本届大赛是由中国信息协会主办 中国信息协会教育分会 艾肯文化传媒 北京 有限公司 中软国际教育科技集团 以太坊行星承办 北京大学研发实验服务基地 iCAN国际联盟 STEERTECH科
  • echarts修改折线图样式,总结踩坑以及常用

    以折线图为例 最终呈现的效果是这样的 在最外层可以设置 距离外层box的距离 myChart setOption grid 距离外层box左右位置 x 30 左 y 45 上 x2 45 右 y2 40 下 borderWidth 1 在x
  • 【WSN无线传感器网络恶意节点】使用 MATLAB 进行无线传感器网络部署研究

    欢迎来到本博客 博主优势 博客内容尽量做到思维缜密 逻辑清晰 为了方便读者 座右铭 行百里者 半于九十 本文目录如下 目录 1 概述 2 运行结果 3 参考文献 4 Matlab代码实现 1 概述 在无线传感器网络部署方面 您计划在一个 1
  • React重新渲染的触发机制及其优化策略

    React是一个用于构建用户界面的JavaScript库 它的核心特点之一是使用虚拟DOM Virtual DOM 来实现高效的组件渲染 那组件重新渲染的机制是如何呢 基于这些机制 如果进行优化呢 虚拟DOM是一个用JavaScript对象
  • 逆向破解学习-登山赛车

    试玩 课程中的内容 Hook代码 import de robv android xposed XC MethodHook import de robv android xposed XposedHelpers import de robv
  • React Router 路由守卫

    React Router 路由守卫 组件内路由守卫 1 下面是使用高阶组件实现路由守卫的示例代码 import React from react import Route Redirect from react router dom con
  • C++设计模式(8)——命令模式

    命令模式 亦称 动作 事务 Action Transaction Command 意图 命令模式是一种行为设计模式 它可将请求转换为一个包含与请求相关的所有信息的独立对象 该转换让你能根据不同的请求将方法参数化 延迟请求执行或将其放入队列中
  • 奇安信笔试C++

    LSA LSA 链路状态通告 是链接状态协议使用的一个分组 它包括有关邻居和通道成本的信息 LSA被路由器接收用于维护它们的路由选择表 LSA Link State Advertisement ip报文最大长度 单看IP层的话 报文头部的
  • JS实现简单的天、时、分、秒倒计时代码

  • Django版本选择、Python兼容问题及更新时间(长期更新)

    先说结果 LTS是长期支持 Long Term Support 的缩写 是官方长期维护的稳定版本 生产环境建议使用LTS版本 最好最好最好不要尝试其他小更新小修补的版本 不做小白鼠 LTS通常是2年内的单数年4月份更新一次 单次版本维护时间
  • 阶段学习总结

    阶段学习总结 从开始学习java到现在已经过了40多天过去了 同时也学习了java中非常重要的一部分就是面向对象的相关知识 面向对象也是java中较难的一部分 因为它是一种抽象的思想 在这一个阶段的学习中学到了很多以前不解的知识点 同时也有
  • java基础知识

    java基础知识 java是一门编程语言 面向对象 是一门开源的计算机语言 曾用名Oak java市场需求 市面比较广 面向对象 可以开发的方面比较多 主要是是应用方面 android app 软件工具 微信小程序 大数据分析 当今社会上手
  • vector的模拟实现

    vector的模拟实现 include