深度剖析vector

2023-05-16

vector 的介绍

  • vector是表示可变大小数组的序列容器
  • 同数组一样,vector采用连续的存储空间来存储元素,因此可以采用下标对vector的元素进行访问,和数组一样高效,与数组不同的是,vector的大小可以动态改变,而且它的大小会被容器自动处理。
  • 本质讲,vector使用动态分配数组来存储它的元素。当新元素插入的时候,这个数组需要被重新分配大小,为了增加存储空间,做法就是,分配一个新的数组,然后将全部的元素移到这个数组。
  • vector的空间分配策略:vector会分配一些额外的空间以适应可能的增长,因为存储空间比实际需要的存储空间更大。
  • 与其他动态序列容器相比,vector在访问元素时更加高效,在末尾添加个删除元素相对高效,但对于其他不在末尾删除和插入的操作效率更低。

vector 的构造函数

1) 无参构造 vector()
2) 构造并初始化n个val vector(size_type n, const value_type& val = value_type())
3) 拷贝构造 vector(const vector& x)
4) 使用迭代器进行初始化构造 vector (InputIterator first, InputIterator last)

int main()
{
	vector<int> v1;//int型的空vector
	vector<int> v2(4, 10);//4个10
	vector<int> v3(v2);//拷贝v3  拷贝构造
	vector<int> v3(v2.begin(), v2.end());//迭代v2中的元素
	return 0;
}

vector 的迭代器

1) 获取第一个数据位置的iterator begin()
2) 获取最后一个数据的下一个位置的iterator end()
3) 获取最后一个数据的iterator rbegin()
4) 获取第一个数据前一个位置的iterator rend()
5) 获取第一个数据位置的const iterator cbegin()
6) 获取最后一个数据的下一个位置的const iterator cend()

在这里插入图片描述
使用迭代器进行遍历打印:

void PrintVector(const vector<int>& v)
{
	//使用const迭代器进行遍历打印,注意const迭代器不能修改vector的内容
	vector<int>::const_iterator it = v.cbegin();
	while (it != v.cend())
	{
		cout << *it << " ";
		++it;
	}
	cout << endl;
}
int main()
{
	//使用push_back
	vector<int> v;
	v.push_back(1);
	v.push_back(2);
	v.push_back(3);
	v.push_back(4);
	//使用迭代器进行遍历打印
	vector<int>::iterator it = v.begin();
	while (it != v.end())
	{
		cout << *it << " ";
		++it;
	}
	cout << endl;
	//使用迭代器进行修改
	it = v.begin();
	while (it != v.end())
	{
		*it *= 2;
		++it;
	}
	PrintVector(v);
	//使用反向迭代器进行遍历打印
	vector<int>::reverse_iterator rit = v.rbegin();
	while (rit != v.rend())
	{
		cout << *rit << " ";
		++rit;
	}
	cout << endl;
	return 0;
}

vector 的空间增长问题

1) 获取数据个数 size()
2) 获取容量大小 capacity()
3) 判断容量是否为空 empty()
4) 改变vector的size void resize(size_type n, value_type val = value_type())
5) 改变vector放入capacity void reserve(size_type n)

  • capacity的代码在vsg++下分别运行会发现,在vscapacity是按1.5倍增长的,在g++下是按2倍增长的。不能固化的认为顺序表增容都是按2倍增长,具体增长多少是根据具体的需求定义的
  • reserve只负责开辟空间,如果确定知道需要多少空间,reserve可以缓解vector增容的代价缺陷问题
  • resize在开辟空间的同时还会进行初始化,影响size的大小
int main()
{
	size_t sz;
	std::vector<int> foo;
	sz = foo.capacity();
	std::cout << "making foo grow:\n";
	for (int i = 0; i < 100; ++i)
	{
		foo.push_back(i);
		if (sz != foo.capacity())
		{
			sz = foo.capacity();
			std::cout << "capacity changed:" << sz << '\n';
		}
	}
	return 0;
}

在这里插入图片描述
可以看出capacity是按照2倍增长的

reserve()测试

void reserveTest2() {
    int sz = 0;
    std::vector<int> bar;
    sz = bar.capacity(); //sz == 0
    bar.reserve(100);
    std::cout << sz << std::endl;
    std::cout << "making bar grow:\n";
    for(int i = 0; i < 100; i++) {
        bar.push_back(i);//99的时候
        if(sz != bar.capacity()) {
            sz = bar.capacity();
            std::cout << "capacity changed:" << sz << std::endl;
        }
    }
}

在这里插入图片描述

resize()测试

std::vector<int> myvector;
for(int i = 1; i < 10; i++) {
    myvector.push_back(i);
}
myvector.resize(5);         //直接截断 1 2 3 4 5
myvector.resize(8,12);   //前5个有数据 默认为原数据 后3个填100
myvector.resize(12);       //默认填0  1 2 3 4 5 12 12 12 0 0 0 0

vector 的增删改查

1) 尾插 void push_back (const value_type& val);
2) 尾删 void pop_back();
3) 查找(这个是算法模块实现,不是vector的成员接口) InputIterator find (InputIterator first, InputIterator last, const T& val);
4) 在pos之前插入val iterator insert (iterator position, const value_type& val);
5) 删除position位置的数据 iterator erase(iterator position;
4) 交换两个vector的数据空间 void swap (vector& x);
5) 像数组空间一样访问 reference operator[] (size_type n);

尾插、尾删测试:

//push_back pop_back
void push_pop_test() {
    int a[] = {1,2,3,4};
    vector<int> vec(a, a + sizeof(a)/ sizeof(int));
    vector<int> :: iterator it = vec.begin();
    while(it != vec.end()) {
        std::cout << *it << " ";
        it++;
    }
    std::cout << std::endl;
    vec.pop_back();
    vec.pop_back();
    it = vec.begin();
    while(it != vec.end()) {
        cout << *it << " ";
        it++;
    }
    cout << endl;
}

查找、删除、插入测试:

void find_insert_eraseTest() {
    int a[] = {1, 2, 3, 4};
    vector<int> vec(a, a + sizeof(a) / sizeof(int));
    //使用find 查找3的位置
    vector<int>::iterator pos = find(vec.begin(), vec.end(), 3);
    //在pos位置之前插入30
    vec.insert(pos,30);
    vector<int>::iterator it = vec.begin();
    while(it != vec.end()) {
        cout << *it << " ";
        it++;
    }
    cout << endl;
    //删除pos的数据
    pos = find(vec.begin(), vec.end(), 3);
    vec.erase(pos);
    it = vec.begin();
    while(it != vec.end()) {
        cout << *it << " ";
        it++;
    }
    cout << endl;

}

vector 的迭代器失效问题

使用erase(iterator)后,后边的每个元素都会失效,但是后边每个元素都会向前移动一个位置,但是erase会返回下一个有效的迭代器

vector 的模拟实现
在这里插入图片描述

namespace gmz {
	template <class T>
	class Vector {
	public:
		typedef T* Iterator;
		typedef const T* ConstIterator;

		Iterator Begin() { return _start; }
		Iterator End() { return _finish; }

		ConstIterator CBegin() const { return _start; }
		ConstIterator CEnd() const { return _finish; }

		size_t Size() const { return _finish - _start; }
		size_t Capacity() const { return _endOfStorage - _start; }

		Vector()
			:_start(nullptr)
			, _finish(nullptr)
			, _endOfStorage(nullptr)
		{}
		Vector(int n, const T& value = T())
			:_start(nullptr)
			, _finish(nullptr)
			, _endOfStorage(nullptr)
		{
			Reserve(n);
			while (n--) {
				PushBack(value);
			}

		}
		template <class InputIterator>
		Vector(InputIterator first, InputIterator last) {
			Reserve(last - first);
			while (first != last) {
				PushBack(*first);
				++first;
			}
		}

		Vector(const Vector<T>& v) :_start(nullptr), _finish(nullptr), _endOfStorage(nullptr)
		{
			Reserve(v.Capacity());
			Iterator it = Begin();
			ConstIterator v_it = v.CBegin();
			while (v_it != v.CEnd()) {
				*it++ = *v_it++;
			}
			_finish = _finish + v.Size();
			_endOfStorage = _finish + v.Capacity();

		}
		void Reserve(size_t n) {
			int t_r = Capacity();
			if (n <= Capacity()) return;

			size_t size = Size();
			T* tmp = new T[n];

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

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

			//否则 就扩容
			if (n > Capacity()) {
				Reserve(n);
			}   
			Iterator it = _finish;
			Iterator _finish = _start + n;

			while (it != _finish) {
				*it = value;
				it++;
			}
		
		}
		void PushBack(const T& x) {
			Insert(End(), x);
		}
		void PopBack() {
			Erase(--End());
		}
		Iterator Insert(Iterator pos, const T& x) {
			assert(pos <= _finish);

			size_t posindex = pos - _start;
			if (_finish == _endOfStorage) {
				size_t size = Size();
				size_t newCapacity = Capacity() == 0 ? 1 : Capacity() * 2;
				Reserve(newCapacity);

				pos = _start + posindex;
			}
			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;
			}
			//++begin;
			--_finish;
			return pos;
		}

		T& operator[](size_t pos) {
			return _start[pos];
		}
		//赋值重载 hansh
		Vector<T>& operator=(const Vector<T>& v) {
			this->Swap(v);
			return *this;
		}
		void Swap(Vector<T>& v) {
			swap(_start, v._start);
			swap(_finish, v._finish);
			swap(_endOfStorage, v._endOfStorage);
		}
		~Vector() {
			delete[] _start;
			_start = nullptr;
			_finish = nullptr;
			_endOfStorage = nullptr;
		}
	private:
		Iterator _start;          //指向数据块的开始
		Iterator _finish;         //指向有效数据的尾
		Iterator _endOfStorage;   //指向存储容量的尾
	};
}
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

深度剖析vector 的相关文章

随机推荐

  • 【Android 源码学习】 init启动

    目录 Android 源码学习 init启动从main cpp开始init cpp 部分逻辑init启动zygote属性服务总结 Android 源码学习 init启动 Android 11 init 启动流程学习 主要是学习刘望舒腾讯课堂
  • Linux面试题 系统启动流程

    BIOS 基本输入输出系统 xff0c 帮助我们初始化硬件 硬盘分区有两类 xff1a MBR和GPT MBR单块硬盘不能大于2T xff0c 主分区的数量不能超过4个 xff1b MBR方案存储在第一个扇区的前446个字节 xff08 共
  • C语言中十进制转换十六进制(细解)

    十进制转换十六进制 span class token macro property span class token directive keyword include span span class token string lt std
  • 关于strtok的使用

    1功能 xff1a strtok是一个比较特殊的 xff0c 用于切割字符串的函数 2 使用我们先来看一下strtok的使用 strtok C 43 43 Reference char strtok xff08 char str xff0c
  • STL简介

    目录 一 STL是什么 二 STL怎么产生的 三 STL的六大组件 一 STL是什么 STL是c 43 43 标准库的重要组成部分 包含数据结构和算法的框架 比如 xff1a vector list sort STL Istream ost
  • SLAM秋招面经(大疆、华为、海康、图森、小马智行、地平线、momenta、滴滴)

    SLAM秋招面经 xff08 大疆 华为 海康 图森 小马智行 地平线 momenta 滴滴 xff09 前段时间一直没更新博客 xff0c 因为论文 实习 秋招一系列事情都非常忙 xff0c 如今秋招接近尾声 xff0c 稍有空闲 xff
  • 基于单目视觉的四旋翼定点降落——概述(一)

    从去年开了这个博客起就一直没有写过 xff0c 好在今天实验有了一些小进展 xff0c 所以想分享给大家 首先讲讲我做了些什么吧 我想做一个基于单目视觉的四旋翼定点降落 xff0c 能够让四旋翼从任意位置起飞之后 xff0c 完全自主地降落
  • 二分查找的经典样例

    对于查找任务来说 xff0c 通常就是寻找目标值或者目标区间出现的位置 而看到排序数组 43 查找 xff0c 第一反应就是二分查找 以下问题都可以直接转化到这个问题上来 34 在排序数组中查找元素的第一个和最后一个位置 查找元素出现的第一
  • 基于单目视觉的四旋翼定点降落——如何通过mavros控制pixhawk(二)

    一直拖了这么久没有更新 xff0c 怪我太懒啦 今天要特别感谢舒仔仔同学 xff0c 我的好队友帮我写了这篇博文 这篇文章主要讲了如何通过mavros来控制pixhawk 硬件逻辑是这样子 xff0c 我们先搞一个odroid xff08
  • 基于单目视觉的四旋翼定点降落——如何搭建基于gazebo的pixhawk仿真环境(三)

    搭建仿真环境是相当重要的 xff0c 因为我们的代码如果直接放到飞机上去跑 xff0c 那么很容易炸机 通过仿真环境 xff0c 我们至少可以保证代码逻辑的正确性 这篇文章还是要感谢我的队友舒仔仔的帮助 xff0c 话不多说 xff0c 上
  • 基于单目视觉的四旋翼定点降落——地标设计与识别算法(四)

    前面讲了三讲的环境搭建 xff0c 今天终于要开始讲算法啦 先来描述一下定点降落的过程 xff0c 四旋翼上搭载的摄像头识别地面上的标志 xff0c 然后以地面标志作为引导 xff0c 降落到标志所在处 所以 xff0c 我们今天先来讲讲地
  • 基于单目视觉的四旋翼定点降落——位姿估算与控制算法(五)

    之前 xff0c 我们已经能够成功将地标识别出来 xff0c 那么如何引导无人机向地标飞行呢 xff1f 我们可以分为两部分 xff1a 一 计算无人机与地标的相对位置 xff1b 二 根据相对位置控制无人机飞行 这部分和之前几章不太一样
  • 利用px4flow实现四旋翼室内定点悬停

    最近博主在玩一个无人机比赛 xff0c 需要完成一些室内飞行的任务 于是 xff0c 为了增强飞行的可靠性 xff0c 用到了px4flow光流模块 xff0c 当然这需要和pixhawk飞控来配套使用 首先简单介绍一下这个模块 xff0c
  • K8s系列之:网络原理

    K8s系列之 xff1a 网络原理 一 K8s网络模型二 Docker的网络模型三 网络的命名空间1 网络命名空间的实现2 网络命名空间的操作3 网络命名空间的一些技巧 四 Veth设备对1 Veth设备对的操作命令2 Veth设备对如何查
  • 用nginx代理tomcat,做https时只需nginx配置证书,443转发到8080即可

    配置如下 server span class token punctuation span listen 80 default server listen span class token punctuation span span cla
  • PB datawindows 动态创建数据窗口

    在实际应用中 xff0c 经常需要根据用户需求来动态创建数据窗 xff0c 一般方法是这样的 在一个window中加入一个数据窗控件 xff0c 如dw new 但是该数据窗没有data object 空白的 就可以用以下语法来创建 xff
  • 一 Prometheus + node_exporter + Grafana 安装和部署

    1 Prometheus Server 端安装 1 1 Promethrus Server 端下载 cd data src wget https github com prometheus prometheus releases downl
  • PHPExcel save Severity: Compile Error --> 'break' not in the 'loop' or 'switch' context /media/sf_

    PHPExcel save出错 xff1a span class hljs attribute Severity span Compile Error span class hljs function gt span span class
  • 关于 C++ 中的 RAII

    名字起得不好 xff0c 实际上是想体现资源管理和对象生命周期绑定 xff0c 在构造函数里获取资源 xff0c 在析构函数里释放资源 RAII 的有趣之处在于利用栈对对象的生命周期进行管理 xff0c 也就间接实现了对资源的管理 xff0
  • 深度剖析vector

    vector 的介绍 vector是表示可变大小数组的序列容器同数组一样 xff0c vector采用连续的存储空间来存储元素 xff0c 因此可以采用下标对vector的元素进行访问 xff0c 和数组一样高效 xff0c 与数组不同的是