首页
学习
活动
专区
圈层
工具
发布
社区首页 >专栏 >C++篇 Vector模拟实现(1) 初始化 迭代器遍历 插入尾插尾删 一文详解

C++篇 Vector模拟实现(1) 初始化 迭代器遍历 插入尾插尾删 一文详解

作者头像
胖咕噜的稞达鸭
发布2025-10-22 14:58:59
发布2025-10-22 14:58:59
1450
举报
文章被收录于专栏:C++初阶高阶C++初阶高阶

vector的使用 在学习vector之前必须了解vector文档中的相关描述;vector的介绍,这里我们挑重点的接口来进行阐述。 //注意:通篇都要看注释!精华总结感悟!!!

1.vector 的定义

构造函数声明

接口说明

vector()

无参构造

vector(size_type n, const value_type& val =value_type())

构造并初始化n个val

vector (const vector& x);

拷贝构造

vector (InputIterator first, InputIterator last);

使用迭代器进行初始化构造

2. vector iterator 的使用

iterator的使用

接口说明

[begin +end]

获取第一个数据位置的iterator/const_iterator, 获取最后一个数据的下一个位置的iterator/const_iterator

rbegin +rend

获取最后一个数据位置的reverse_iterator,获取第一个数据前一个位置的reverse_iterator

3.vector 空间增长问题

容量空间

接口说明

size

获取数据个数

capacity

获取容量大小

empty

判断是否为空

resize

改变vector的size

reserve

改变vector的capacity

4.vector 增删查改

vector增删查改

接口说明

push_back

尾插

pop_back

尾删

find

查找。(注意这个是算法模块实现,不是vector的成员接口)

insert

在position之前插入val

erase

删除position位置的数据

swap

交换两个vector的数据空间

operator[]

像数组一样访问

vector初始化

代码语言:javascript
复制
vector<int>v1;//无参数的构造
	vector<int>v2(10, 1);//带参数的构造,初始化1个数组,
	vector<int>v3(++v2.begin(), --v2.end());//还可以用一段迭代器区间去初始化
	//v3用v2去初始化,不想要第一个,也不想用最后一个,可以向上面那么写。v3有8个1

vector遍历

代码语言:javascript
复制
	//遍历v3,下标第一种遍历方法
	for (size_t i = 0; i < v3.size(); i++)
	{
		cout << v3[i] << " ";
	}
	cout << endl;
	//用迭代器去遍历,要指定类域,实例化的模板参数,第二种遍历
	vector<int>::iterator it = v3.begin();
	while (it != v3.end())
	{
		cout << *it << " ";
		it++;
	}
	cout << endl;
	
	//第三种遍历,范围for
	for (auto e : v3)
	{
		cout << e << " ";
	}
	cout << endl;

vector的扩容

代码语言:javascript
复制
void test_vector2()
{
	vector<int>v(10, 1);//初始化10个1
	v.reserve(20);//扩容到20
	cout << v.capacity() << endl;//20
	cout << v.size() << endl;//10

	v.reserve(15);
	cout << v.capacity() << endl;//20
	cout << v.size() << endl;//10

	v.reserve(5);
	cout << v.capacity() << endl;//20
	cout << v.size() << endl;//10    //坚决不缩容,capacity在增加到20之后,就没有变化了
}

void test_vector3()
{
	vector<int>v(10.1);
	v.reserve(20);//扩容到capacity=20,size是10,capacity是20
	cout << v.capacity() << endl;//20
	cout << v.size() << endl;//10

	v.resize(15.2);//10个1,补了5个2(调试过程中),capacity=20
	cout << v.size() << endl;
	cout << v.capacity() << endl;

	v.resize(25.3);//capacity是30,按照2倍扩容,10个1,size=25
	cout << v.size() << endl;
	cout << v.capacity() << endl;

	v.resize(5);//capacity=30,size=5,只保留5个数据,5个1
	cout << v.size() << endl;
	cout << v.capacity() << endl;
}

vector遍历和流提取

代码语言:javascript
复制
void test_vector4()
{
	vector<int>v(10, 1);
	v.push_back(2);
	v.insert(v.begin(), 0);

	//遍历要用迭代器
	for (auto e : v)
	{
		cout << e << " ";// 0  1  1  1  1  1   1   1   1   1   1   2
	}
	cout << endl;

	v.insert(v.begin() + 3, 10);//在第3个位置加一个10

	for (auto e : v)
	{
		cout << e << " ";// 0  1  1  10  1   1  1   1   1   1   1   1   2
	}
	cout << endl;

	v.clear();//实现vector的流插入流提取,方法1
	int x;
	cin >> x;
	v.push_back(x);

	vector<int> v1(5, 0);//给5个数据初始化为0,方法2:用范围for进行遍历
	for (size_t i = 0; i < 5; i++)
	{
		cin >> v1[i];//cin输入v1[i];
	}
	for (auto e : v1)
	{
		cout << e << " ";//用范围for进行遍历,输入的数字用空格分割,最后打印出55个数字
	}
	cout << endl;
}

这里我们就有疑问了,string s2可不可以用vectorv2进行替代? 不可以! 1.string s2最后有\0,但是vectorv2没有\0 2.string可以支持比较大小,要按照ASCII码表进行比较,但是vector没有

vector的二维数组构建

代码语言:javascript
复制
void test_vector5()
{
	vector<string>v1;//vector中还可以存储自定义类型
	string s1("xxxxxxx");

	v1.push_back(s1);
	v1.push_back("yyyyyy");//const char* 还可以隐式类型转换
	for (const auto& e : v1)//*it取每个值,都是一个string,string给e,进行每次拷贝构造
		//有多少个string对象就要拷贝构造多少次,所以加& ,不改变遍历的值前面加const
		//以前都是拷贝一下Int,代价比较小,如果要拷贝string,不仅要开空间还要调用数据

	{
		cout << e << " ";
	}
	cout << endl;


	//假如要实现10*5的二维数组
	//vector<vector<int>>vv();//vector里面存vector,就是二维数组
	vector<int>v(5, 1);
	vector<vector<int>>vv(10, v);//10个v,v是v(5,1)
	//vv[2][1] = 2;//访问第二行第一个并且将其修改为2    ///看图!!!
	//编译器实例化了两个vector,
	vv.operator[](2).operator[](1) = 2;//等价于vv[2][1]=2;
	for (size_t i = 0; i < vv.size(); i++)
	{
		for (size_t j = 0; j < vv[i].size(); j++)
		{
			cout << vv[i][j] << " ";
		}
		cout << endl;
	}
	cout << endl;

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

这里我们用C++做一道算法题来熟悉一下vector二维数组动态规划在算法题中的应用:杨辉三角OJ

在这里插入图片描述
在这里插入图片描述
代码语言:javascript
复制
clase Solution{
public:
	vector<vector<int>>generate(int numsRow)
	{
		vector<vector<int>>vv(numsRow);//开n行
		for (size_t i = 0; i < numsRow; ++i)
		{
			vv[i].resize(i + 1, 0);//第0行(索引)有1个,第1行(索引)有2个,每个数据默认给0
			//vv[i][0]=vv[i][vv[i].size()-1]=1;
			vv.front = vv.back = 1;
		}
		for (int i = 2; i < vv.size(); ++i)//第0行和第1行不需要
		{
			for (int j = 1; j < vv[i].size()-1; ++j)//避开每一行的第一个数据和组后一个数据
			{
				vv[i][j] = vv[i - 1][j] + vv[i - 1][j - 1];//上一行的当前位置+上一行的前一个位置
			}
		}
		return vv;
	}
}

迭代器遍历,operator[]访问

代码语言:javascript
复制
#pragma once
#include<iostream>
#include<assert.h>
using namespace std;
namespace Keda
{
	template<class T>
	class vector
	{
	public:
		typedef T* iterator;//使用typedef为T*(指向模板类型T的指针)定义了一个
		//别名iterator,用于遍历容器中的元素。表示vector类的迭代器是一个指向T类型的指针
		typedef const T* const_iterator;//const不可修改型

		iterator begin()
		{
			return _start;
		}
		iterator end()
		{
			return _finish;
		}

		const_iterator begin()const
		{
			return _start;
		}
		const_iterator end()const
		{
			return _finish;
		}
		size_t size()const
		{
			return _finish - _start;
		}
		size_t capacity()const
		{
			return _end_of_storage - _start;
		}

		T& operator[](size_t i)//遍历
		{
			assert(i < size());
			return _start[i];
		}
		
		const T& operator[](size_t i)const
		{
			assert(i < size());
			return _start[i];
		}


	private:
		iterator _start = nullptr;
		iterator _finish = nullptr;//有效元素的末尾位置的下一个位置
		iterator _end_of_storage = nullptr;//用于判断是否需要扩容,意为空间用完了
	};
}

vector模板

代码语言:javascript
复制
//模板
template<class T>
void print_vector(const vector<T>& v)//const对象就要用const迭代器
{
	//在没有实例化的类模板里面取东西,规定:没有实例化的类模板里面取东西
	//编译器不能区分这里的const_iterator是类型还是静态成员变量
	//typename vector<T>::const_iterator it = v.begin();//也可以直接由编译器自己推导
	//静态成员变量不需要有类型
	auto it = v.begin();
	while (it != v.end())
	{
		cout << *it << " ";
		++it;
	}
	cout << endl;

	for (auto e : v)
	{
		cout << e << " ";
	}
	cout << endl;
}

vector插入!!!(内设及迭代器失效问题!!必看)

代码语言:javascript
复制
void reserve(size_t n)
{
	if (n > capacity())
	{
		size_t old_size = size();
		T* tmp = new T[n];//开新的空间
		memcpy(tmp, _start, size() * sizeof(T));//拷贝旧空间的数据给新的空间
		delete[]_start;//释放掉旧的空间

		_start = tmp;//指向新的空间
		_finish = tmp + old_size;
		_end_of_storage = tmp + n;
	}

}

void push_back(const T& x)
{
	if (_finish != _end_of_storage)
	{
		*_finish = x;
		++_finish;
	}
	else
	{
		reserve(capacity() == 0 ? 4 : capacity() * 2);
		*_finish = x;
		++_finish;
	}
	iterator insert(iterator pos, const T& x)
	{
		if (_finish == _end_of_storage)
		{
			size_t len = pos - _start;
			reserve(capacity() == 0 ? 4 : 2 * capacity());
			pos = len + _start;
		}

		iterator end = _finish - 1;
		while (end >= pos)//可以等于0
		{
			*(end + 1) = *(end);
			--end;
		}
	*pos = x;
	++_finish;
	return pos;
}

这里会出现迭代器失效的问题: 分析:当需要插入一个新的x,很有可能出现_finish==_end_of_storage的情况,需要扩容开辟一个新的tmp指针指向一块新的空间,然后释放掉旧的_start,让tmp所在的这块空间称为新_start,然后构建好新的_finish,_end_of_storage 为什么:但是!!!!!pos还指向的是旧_start,扩容的时候如果没有考虑到pos,释放旧的_start的时候会将pos一并释放,所以再插入这里使用的pos是一个野指针 解决办法就是找到新的_start中对应旧_start的pos位置 所以在扩容的地方算出相对len,size_t len=pos-_start;扩容好了之后更新pos即可: pos=len+_start;

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

续:测试插入

代码语言:javascript
复制
void test_vector2()
{
	vector<int>v;
	v.push_back(1);
	v.push_back(2);
	v.push_back(3);
	v.push_back(4);
	//v.push_back(5);
	print_vector(v);

	v.insert(v.begin() + 3, 45);
	print_vector(v);

	//迭代器区间要求传左闭右开
	int x;
	cin >> x;
	auto pos = find(v.begin(), v.end(), x);
	if (pos != v.end())
	{
		//v.insert(pos, 40);
		//查找x,找到之后让pos*=10,假如pos=2,我们乘等10,应该输出20
		//(*pos) *= 10;//实际上输出400,为什么?这里也是迭代器失效的问题,所以insert以后pos就失效了,不要访问
		//pos还指向的是旧空间,再解引用就是越界的野指针,即使在insert里面做了改变,已经更新过了,但是毕竟是在内部,实参的改变不影响形参


		//那要是一定要哦访问呢???
		//这里可以将insert操作完之后在内部操作的值pos返回,iterator insert (iterator pos,const T& s); 下面return pos
		//在测试中:pos=v.insert(pos,40); (*(p+1)) *=10

		pos = v.insert(pos, 40);
		(*(pos+1) ) *= 10;
	}
	print_vector(v);

}

这里也是迭代器失效的问题,insert以后pos就失效了,不要访问; pos还指向的是旧空间,再解引用就是越界的野指针,即使在insert里面做了改变,已经更新过了,但是毕竟是在内部,实参的改变不影响形参(内部的更新不影响外部)

vector尾删

代码语言:javascript
复制
bool empty()
{
	return _start == _finish;
}
void pop_back()
{
	assert(!empty());
	--_finish;
}
本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2025-09-30,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 1.vector 的定义
  • 2. vector iterator 的使用
  • 3.vector 空间增长问题
  • 4.vector 增删查改
  • vector初始化
  • vector遍历
  • vector的扩容
  • vector遍历和流提取
  • vector的二维数组构建
  • 迭代器遍历,operator[]访问
  • vector模板
  • vector插入!!!(内设及迭代器失效问题!!必看)
  • vector尾删
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档