C++ vector的模拟实现

一 vector的大致框架

1.1 框架

 vector的成员变量不再是我们熟悉的size,capacity,而是变成了功能一致的三个指针:_start,_finish,_endofstorage,三个指针的作用如下:

同时,因为其本身指针的特性,其迭代器也是返回其内部的指针就可以了,因此我们可以直接定义迭代器。

 大致框架如下:
 

namespace My
{template<class T>class vector{public:typedef T* iterator;typedef const T* const_iterator;private:iterator _start;iterator _finish;iterator _endofstorage;};
}

1.2 默认构造函数 

 实际上,我们可以在底层初始化给三个指针都赋值为nullptr,这样默认构造就可以什么都不敢,并且在初始化时就全都赋值为nullptr,可以使代码更加简练。

代码如下:
 

	//构造函数vector() {}
private:iterator _start = nullptr;iterator _finish = nullptr;iterator _endofstorage = nullptr;

1.3 析构函数

delete掉空间,将三个指针再次赋值为nullptr即可。

 

	//析构 ~vector() {delete[] _start; //注意一定要加[],因为有可能存的值是一个数组_start = _finish = _endofstorage = nullptr;}

二 vector的容量操作

2.1 size函数

 一般的容器,我们都有size和capacity两个函数 来获取其容器大小,在vector中我们也有这两个函数,这里我们通过 指针-指针的差值来 计算其实际值。

 代码如下:
 

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

2.2 capacity函数

 代码如下:

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

2.3 reserve函数

  在库中,reserve 修改容量时,size 是不会发生变化的(因为有效元素个数不变),若空间容量扩大则 capacity 会相应的进行扩大,若空间容量缩小时 capacity 是不会发生变化的。

  reserve的思想就是把原来的空间舍弃掉,重新开一段大空间

代码如下:
 

	//扩容void reserve( size_t n) {if (n > capacity()) {//开始扩容T* tmp = new T[n];size_t sz = size();//提前算出size(),用来把原值赋值给新空间if (_start) {for (size_t i = 0; i < sz; i++) {tmp[i] = _start[i];}delete[] _start;//避免内存泄漏}//指针指向新空间_start = tmp;_finish = _start + sz;_endofstorage = _start + n;}}

2.4 resize函数

 resize 修改有效元素个数时,size 会进行相应的扩大或缩小变化,capacity 不一定会变化(有效元素增多时,若容量足够则capacity不变,否则会扩容;有效元素减少时,capacity 不变)

 resize的两个操作如下:

  • 1.如果n小于当前的size,则数据个数缩小到n
  • 2.如果n大于于当前的size,则说明空间不够需要增容,并且将size扩大到n

  注意,这里val得用T的默认构造。 

 代码示例:

	//resizevoid resize(size_t n, const T& val = T()) {if (n <= size()) {_finish = _start + n;}else {reserve(n);while (_finish < _start + n) {*_finish = val;++_finish;}}}

三迭代器 

  vector的迭代器就是原生指针,因此直接返回指针即可:
 

typedef T* iterator;
typedef const T* const_iterator;iterator begin() {return _start;
}iterator end() {return _finish;
}const_iterator begin() const {return _start;
}const_iterator end() const {return _finish;
}

四 元素修改函数

4.1 insert函数

 在指定pos位置进行插入元素

 但是这里却有一个很坑的点,那就是不小心写出来的代码就会引起迭代器失效的问题。

  因为插入后,我们可能会发生扩容,pos指针会失效,这里我们先记录一下pos位置和起始位置的差值,扩容后让pos指针指向新空间。

	void insert(iterator pos, const T& val) {assert(pos >= _start);assert(pos <= _finish);if (_finish == _endofstorage) {size_t len = pos - _start; reserve(capacity() == 0 ? 4 : capacity() * 2);pos = _start + len;}iterator end = _finish - 1;while (end >= pos) {*(end + 1) = *end;--end;}*pos = val;++_finish;}

但要注意,这样的话,外部的pos指针怎么办呢?

这里是值传递,形参的改变是不会改变实参的,所以说外面的pos依旧没有实现更新操作,依旧会失效。

正确写法是通过返回值去解决问题的,返回的是迭代器,即新插入元素的位置。

正确代码为:
 

	iterator insert(iterator pos, const T& val) {assert(pos >= _start);assert(pos <= _finish);if (_finish == _endofstorage) {size_t len = pos - _start;reserve(capacity() == 0 ? 4 : capacity() * 2);pos = _start + len;}iterator end = _finish - 1;while (end >= pos) {*(end + 1) = *end;--end;}*pos = val;++_finish;return pos;}

4.2 erase()

  这里主要的就是从后往前删除的,这点需要大家注意一下。

  同时,erase也会发生迭代器失效问题,因此要返回值避免迭代器失效。

代码如下:
 

	iterator erase(iterator pos) {assert(pos >= _start);assert(pos < _finish);iterator it = pos + 1;while (it < _finish) {*(it - 1) = *it;++it;}--_finish;return pos;}

4.3 push_back()

插入尾部元素,直接复用insert.

void push_back(const T& val) {insert(end(), val);
}

4.4 pop_back() 

删除尾部元素,直接把_finish--就好。

	void pop_back(){assert(_start); //注意判空--_finish;}

五 元素访问

 5.1 []的重载

  直接放回数组中的数据即可

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

六 其它函数 

vector(size_t n, const T& val = T()) {reserve(n);for (size_t i = 0; i < n; i++) {push_back(val);}
}vector(int n, const T& val = T()) {reserve(n);for (int i = 0; i < n; i++) {push_back(val);}
}//区间构造
template <class InputIterator>
vector(InputIterator first, InputIterator last) {while (first != last) {push_back(*first);++first;}
}void swap(vector<T>& v) {std::swap(v._start, _start);std::swap(v._finish, _finish);std::swap(v._endofstorage, _endofstorage);
}//拷贝构造
vector(const vector<T>& v) {reserve(v.capacity());for (auto& x : v) {push_back(x);}
}//移动构造
vector(vector<T>&& v) {swap(v);
}//赋值重载
vector<T>& operator=(const vector<T>& v) {vector<T> tmp(v);swap(tmp);return *this;
}
//移动赋值
vector<T>& operator=(vector<T>&& v) {swap(v);return *this;
}

七 完整代码和测试代码

 

#pragma once
#include<iostream>
#include<cassert>using namespace std;namespace My {template<class T>class vector {public:typedef T* iterator;typedef const T* const_iterator;iterator begin() {return _start;}iterator end() {return _finish;}const_iterator begin() const {return _start;}const_iterator end() const {return _finish;}//构造函数vector() {}vector(size_t n, const T& val = T()) {reserve(n);for (size_t i = 0; i < n; i++) {push_back(val);}}vector(int n, const T& val = T()) {reserve(n);for (int i = 0; i < n; i++) {push_back(val);}}//区间构造template <class InputIterator>vector(InputIterator first, InputIterator last) {while (first != last) {push_back(*first);++first;}}void swap(vector<T>& v) {std::swap(v._start, _start);std::swap(v._finish, _finish);std::swap(v._endofstorage, _endofstorage);}//拷贝构造vector(const vector<T>& v) {reserve(v.capacity());for (auto& x : v) {push_back(x);}}//移动构造vector(vector<T>&& v) {swap(v);}//赋值重载vector<T>& operator=(const vector<T>& v) {vector<T> tmp(v);swap(tmp);return *this;}//移动赋值vector<T>& operator=(vector<T>&& v) {swap(v);return *this;}//扩容void reserve( size_t n) {if (n > capacity()) {//开始扩容T* tmp = new T[n];size_t sz = size();//提前算出size(),用来把原值赋值给新空间if (_start) {for (size_t i = 0; i < sz; i++) {tmp[i] = _start[i];}delete[] _start;//避免内存泄漏}//指针指向新空间_start = tmp;_finish = _start + sz;_endofstorage = _start + n;}}//resizevoid resize(size_t n, const T& val = T()) {if (n <= size()) {_finish = _start + n;}else { reserve(n);while (_finish < _start + n) {*_finish = val;++_finish;}}}iterator insert(iterator pos, const T& val) {assert(pos >= _start);assert(pos <= _finish);if (_finish == _endofstorage) {size_t len = pos - _start;reserve(capacity() == 0 ? 4 : capacity() * 2);pos = _start + len;}iterator end = _finish - 1;while (end >= pos) {*(end + 1) = *end;--end;}*pos = val;++_finish;return pos;}void push_back(const T& val) {insert(end(), val);}iterator erase(iterator pos) {assert(pos >= _start);assert(pos < _finish);iterator it = pos + 1;while (it < _finish) {*(it - 1) = *it;++it;}--_finish;return pos;}void pop_back(){assert(_start); //注意判空--_finish;}T& operator[](size_t pos) {assert(pos < size());return _start[pos];}const T& operator[](size_t pos) const {assert(pos < size());return _start[pos];}size_t capacity() const {return _endofstorage - _start;}size_t size() const {return _finish - _start;}//析构 ~vector() {delete[] _start; //注意一定要加[]_start = _finish = _endofstorage = nullptr;}private:iterator _start = nullptr;iterator _finish = nullptr;iterator _endofstorage = nullptr;};void test_vector1(){vector<int> v;v.push_back(1);v.push_back(2);v.push_back(3);v.push_back(4);v.push_back(5);for (size_t i = 0; i < v.size(); i++){cout << v[i] << " ";}cout << endl;vector<int>::iterator it = v.begin();while (it != v.end()){*it *= 10;cout << *it << " ";++it;}cout << endl;for (auto e : v){cout << e << " ";}cout << endl;}void test_vector2(){int i = 0;int j(1);int k = int(2);vector<int*> v1;v1.resize(10);vector<string> v2;//v2.resize(10, string("xxx"));v2.resize(10, "xxx");for (auto e : v1){cout << e << " ";}cout << endl;for (auto e : v2){cout << e << " ";}cout << endl;}void test_vector3(){vector<int> v;v.push_back(1);v.push_back(2);v.push_back(3);v.push_back(4);v.push_back(5);v.push_back(6);v.push_back(7);for (auto e : v){cout << e << " ";}cout << endl;vector<int>::iterator it = v.begin() + 2;v.insert(it, 30);for (auto e : v){cout << e << " ";}cout << endl;//v.insert(v.begin(), 30);v.insert(v.begin() + 3, 30);for (auto e : v){cout << e << " ";}cout << endl;}void test_vector4(){vector<int> v;v.push_back(1);v.push_back(2);v.push_back(3);v.push_back(4);v.push_back(5);v.push_back(6);v.push_back(7);for (auto e : v){cout << e << " ";}cout << endl;auto pos = v.begin();v.erase(pos);for (auto e : v){cout << e << " ";}cout << endl;v.erase(v.begin() + 3);for (auto e : v){cout << e << " ";}cout << endl;}//void test_vector5()//{//	// 1 2 3 4 5//	// 1 2 3 4 5 6//	// 2 2 3 4 5//	std::vector<int> v;//	v.push_back(1);//	v.push_back(2);//	v.push_back(3);//	v.push_back(4);//	v.push_back(5);//	//v.push_back(6);//	for (auto e : v)//	{//		cout << e << " ";//	}//	cout << endl;//	auto it = v.begin();//	while (it != v.end())//	{//		// vs2019进行强制检查,erase以后认为it失效了,不能访问,访问就报错//		if (*it % 2 == 0)//		{//			v.erase(it);//		}//		++it;//	}//	for (auto e : v)//	{//		cout << e << " ";//	}//	cout << endl;//}void test_vector5(){//std::vector<int> v;vector<int> v;v.push_back(1);v.push_back(2);v.push_back(3);v.push_back(4);v.push_back(4);v.push_back(5);v.push_back(6);for (auto e : v){cout << e << " ";}cout << endl;auto it = v.begin();while (it != v.end()){if (*it % 2 == 0){it = v.erase(it);}else{++it;}}for (auto e : v){cout << e << " ";}cout << endl;}void test_vector6(){vector<string> v;v.push_back("111111111111111111111");v.push_back("111111111111111111111");v.push_back("111111111111111111111");v.push_back("111111111111111111111");v.push_back("111111111111111111111");for (auto e : v){cout << e << " ";}cout << endl;}void test_vector7(){vector<int> v1;v1.push_back(1);v1.push_back(1);v1.push_back(1);v1.push_back(1);v1.push_back(1);vector<int> v2(v1);for (auto e : v1){cout << e << " ";}cout << endl;for (auto e : v2){cout << e << " ";}cout << endl;vector<int> v3;v3.push_back(10);v3.push_back(20);v3.push_back(30);v3.push_back(40);v1 = v3;for (auto e : v1){cout << e << " ";}cout << endl;}void test_vector8(){//vector<int> v0(10, 0);vector<string> v1(10, "xxxx");for (auto e : v1){cout << e << " ";}cout << endl;vector<int> v2;v2.push_back(10);v2.push_back(20);v2.push_back(30);v2.push_back(40);vector<int> v3(v2.begin(), v2.end());string str("hello world");vector<int> v4(str.begin(), str.end());for (auto e : v3){cout << e << " ";}cout << endl;for (auto e : v4){cout << e << " ";}cout << endl;}}

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

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

工具:meson+ninja(安装问题解决)

问题1&#xff1a;Python版本问题 报错信息&#xff1a; NOTICE: You are using Python 3.6 which is EOL. Starting with v0.62.0, Meson will require Python 3.7 or newer ubuntu 18默认的python3是3.6. 解决方案1&#xff1a;从源码安装python 3.7 wget https://www.pyth…

阿里云江苏省中小企业补贴5000元上云补贴金

阿里云「数智惠企」中小企业补贴&#xff0c;江苏区域企业提交申请内部评估及审批通过后&#xff0c;即可获取上云补贴金&#xff0c;使用补贴金购买指定云产品&#xff0c;满10000元即可立减5000元&#xff0c;请抓紧申领。阿里云百科 aliyunbaike.com 分享江苏区域5000元上云…

Postman创建及删除workspace工作空间

文章目录 一、Postman创建workspace工作空间二、Postman删除workspace工作空间 一、Postman创建workspace工作空间 打开Postman 点击 Workspaces → Create Workspaces 如图所示操作 工作空间创建完成 二、Postman删除workspace工作空间 点击 Workspaces → 选择要删除…

【pynput】鼠标行为追踪并模拟

文章目录 前言基本思路安装依赖包实时鼠标捕获捕获鼠标位置捕获鼠标事件记录点击内容 效果图 利用本文内容从事的任何犯法行为和开发与本人无关&#xff0c;请理性利用技术服务大家&#xff0c;创建美好和谐的社会&#xff0c;让人们生活从繁琐中变得更加具有创造性&#xff01…

欠采样对二维相位展开的影响

1.前言 如前所述&#xff0c;相位展开器通过计算两个连续样本之间的差来检测图像中包裹的存在。如果这个差值大于π或小于-π&#xff0c;则相位展开器认为在这个位置存在包裹。这可能是真正的相位包络&#xff0c;也可能是由噪声或采样不足引起的伪包络。 对欠采样的相位图像…

LangChain 33: LangChain表达语言LangChain Expression Language (LCEL)

LangChain系列文章 LangChain 实现给动物取名字&#xff0c;LangChain 2模块化prompt template并用streamlit生成网站 实现给动物取名字LangChain 3使用Agent访问Wikipedia和llm-math计算狗的平均年龄LangChain 4用向量数据库Faiss存储&#xff0c;读取YouTube的视频文本搜索I…

springMVC-与spring整合

一、基本介绍 在项目开发中&#xff0c;spring管理的 Service和 Respository&#xff0c;SrpingMVC管理 Controller和ControllerAdvice,分工明确 当我们同时配置application.xml, springDispatcherServlet-servlet.xml , 那么注解的对象会被创建两次&#xff0c; 故…

Hadoop入门学习笔记——三、使用HDFS文件系统

视频课程地址&#xff1a;https://www.bilibili.com/video/BV1WY4y197g7 课程资料链接&#xff1a;https://pan.baidu.com/s/15KpnWeKpvExpKmOC8xjmtQ?pwd5ay8 Hadoop入门学习笔记&#xff08;汇总&#xff09; 目录 三、使用HDFS文件系统3.1. 使用命令操作HDFS文件系统3.1.…

Linux系统安装及管理

目录 一、Linux应用程序基础 1.1应用程序与系统命令的关系 1.2典型应用程序的目录结构 1.3常见的软件包装类型 二、RPM软件包管理 1.RPM是什么&#xff1f; 2.RPM命令的格式 2,1查看已安装的软件包格式 2.2查看未安装的软件包 3.RPM安装包从哪里来&#xff1f; 4.挂…

VS(Visual Studio)更改文件编码

vs默认编码是GB2312,更改为UTF-8 工具->自定义

ansible的控制语句

本章内容主要介绍 playbook 中的控制语句 使用when判断语句block-rescue判断循环语句 一个play中可以包含多个task&#xff0c;如果不想所有的task全部执行&#xff0c;可以设置只有满足某个条件才执行这个task&#xff0c;不满足条件则不执行此task。本章主要讲解when 和 blo…

初识QT(上篇):What Qt

初识QT&#xff08;上篇&#xff09;&#xff1a;What Qt 前言 & 说明前言说明 初识QT1.1 QT的what1. 介绍2. 发展历程3. QT架构的主要内容4.QT的常用模块 1.2 QT的 why1. QT的核心机制 下篇笔记链接 前言 & 说明 前言 前言&#xff1a; 之前说要share的qt相关知识&am…