vector

目录

vector的成员函数:

at:

​编辑

 size:

assign:赋值

insert

find?

erase

swap

shrink_to_fit

​编辑

 vector的模拟实现:

vector的框架:

构造函数:

size和capacity

 reserve函数:

begin和end

[]

尾插函数:

 const修饰的begin和end

const修饰的[]

empty函数:

resize:

尾删

insert:

标准库里面的find函数:


vector的成员函数:

at:

vector_第1张图片

 at表示访问vector的第n个位置的数据。

我们进行实验:

#include
#include
#include
using namespace std;

void vector_test1()
{
	vector v;
	v.resize(10, 5);
	for (int i = 0; i < v.size(); i++)
	{
		cout << v.at(i) << " ";
	}
	cout << endl;
}
int main()
{
	vector_test1();
	return 0;
}

我们使用resize对v进行初始化,初始化的结果是vector有是个整型元素,十个元素都是5,我们遍历v,使用at函数,打印vector的每一个元素。

 size:

vector_第2张图片

size函数的返回值是vector中有效元素的个数。

void func(const vector&v)
{
	v.size();
}
void test_vector2()
{
	vector v;
	v.push_back(1);
	v.push_back(2);
	v.push_back(3);
	v.push_back(4);
	v.push_back(5);
	v.size();
	func(v);
}
int main()
{
	/*vector_test1();*/
	test_vector2();
	return 0;
}

如图所示的函数,为什么func这里需要传引用呢?

答:因为我们的vector中存储的数据类型不仅可能是内置类型,也有可能是自定义类型,传值传参的过程本身就是拷贝,对于内置类型,拷贝的消耗不大,但是对于自定义类型,拷贝的消耗就很大,所以我们用传引用传参提高效率。

如图所示,为什么传参时需要加上const呢?

答:因为我们是传引用传参,我们得到v是为了读取到v上的size(),假如我们不加const,我们对v或v上的数据进行修改时,v本身也会做出相应的变化,所以我们加上const,表示我们的v是只读的,防止函数内部对v进行修改。

 如图所示:这两个size函数的调用是同一个size吗?

vector_第3张图片

 答:如图所示:

vector_第4张图片

是同一个size,因为我们的size只有一个接口函数,加上const的原因是我们的size是vector的基础属性,我们可以读取size,但是不能对size进行修改。我们在传参时,发生了权限的缩小,我们的v在test_vector2函数中是可读可写的,但是到了func函数中,我们的v变成了只读函数。

vector_第5张图片

 同样是vector函数,为什么size函数只有一个接口函数,而[]函数却有两个接口函数?

void func(const vector&v)
{
	v[0];
	v[0]++;
}
void test_vector2()
{
	vector v;
	v.push_back(1);
	v.push_back(2);
	v.push_back(3);
	v.push_back(4);
	v.push_back(5);
	v[0];
	v[0]++;
	func(v);
}
int main()
{
	/*vector_test1();*/
	test_vector2();
	return 0;
}

 我们进行编译:

 func的v[0]++出现错误:原因如下:

答:在test_vector2中,因为我们的v是可读可写的,所以我们的[]匹配的就是这个函数:

 在func2中,因为我们的v被const修饰,所以我们的v是只读的,我们调用[]函数匹配的就是这个函数:

 因为我们的[]被const修饰,所以我们只能读而不能修改。

总结:1:只读函数,const例如:size()

我们的size()函数本身就是只读的,所以我们用可读的对象或者可读可写的对象都可以调用size()函数。

2:只写函数,非const  例如:push_back()

vector_第6张图片

 我们的push_back函数必须要求调用函数的对象必须是可写的,也就是说const修饰的对象不能调用push_back函数。

3:可读可写的函数  例如:[]和at()函数

 我们这些函数有两个接口函数,当我们调用函数的对象是可读可写的,我们就调用第一个函数,假如我们调用函数的对象是只读的,我们就调用第二个函数。

 问题:at()和[]函数的区别在哪里?

我们先写一串代码:

void test_vector3()
{
	vector v;
	v.reserve(10);
	for (size_t i = 0; i < 10; ++i)
	{
		v[i] = i;
	}
}
int main()
{
	/*vector_test1();*/
	test_vector3();
	return 0;
}

我们进行运行会报错:

vector_第7张图片

这里报的错误是断言错误,原因如下:我们虽然reserve开了10个空间,但是我们并没有resize,所以我们的size依旧是0,我们的[]内部的实现里有一个断言函数:要求[i]中的i要小于size,我们的size为0,所以不满足断言,所以会报错。

 假如我们把这里的[]换成at呢?还会报错吗?

vector_第8张图片

 依旧会报错,但是这里报错的形式是抛异常。

总结:at和[]的不同点在于报错的形式:

at是通过抛异常的形式进行报错。

[]是通过断言的形式进行报错。

断言报错是有缺点的,因为assert只有在Debug版本下才能生效,在release版本下会失效。

assign:赋值

vector_第9张图片

 assign有两种写法,第一种写法是以迭代器区间的形式进行赋值,第二种就是普通的赋值。

问题1:为什么这里需要用类摸板,为什么不直接用迭代器。

答:原因是我们的vector中不仅会有内置类型,也可能会有自定义类型,对于自定义类型的迭代器,我们可以用类模板实现。

我们对两种函数进行实验:

void test_vector3()
{
	vector v;
	v.push_back(1);
	v.push_back(2);
	v.push_back(3);
	v.assign(10, 1);
	for (auto e : v)
	{
		cout << e << " ";
	}
	cout << endl;
}
int main()
{
	/*vector_test1();*/
	test_vector3();
	return 0;
}

 assign表示把vector的前十个元素都赋值为1.

assign的第二种应用。

void test_vector3()
{
	vector v,v1;
	v.push_back(1);
	v.push_back(2);
	v.push_back(3);
	v1.assign(v.begin(), v.end());
	for (auto e : v1)
	{
		cout << e << " ";
	}
	cout << endl;
}
int main()
{
	/*vector_test1();*/
	test_vector3();
	return 0;
}

assign这里表示把从迭代器v.begin()到v.end()的全部元素都赋值给v1.

我们进行调用。

 接下来,我们尝试用string进行赋值。

void test_vector3()
{
	/*vector v,v1;
	v.push_back(1);
	v.push_back(2);
	v.push_back(3);*/
	vector v;
	string str("hello world");
	v.assign(str.begin(), str.end());
	//v1.assign(v.begin(), v.end());
	for (auto e : v)
	{
		cout << e << " ";
	}
	cout << endl;
}
int main()
{
	/*vector_test1();*/
	test_vector3();
	return 0;
}

我们把迭代器str.begin()到str.end()的元素赋值给v,我们进行打印。

 之所以是数字:我们的vector内的数据类型是int,所以把string中的char类型强制转换成为了int,变成了数字。

insert

vector_第10张图片

 我们的string的insert函数的参数使用的是下标,而我们的vecor的insert使用的都是迭代器。

我们在vector中不提供头插和头删,原因是头插和头删需要挪动数据,造成效率降低。

如果我们真的想要使用头插或者头删时,我们可以使用insert和erase来代替。

例如:

void test_vector3()
{
	vector v;
	v.reserve(10);
	v.insert(v.begin(), 1);
	for (int i = 0; i < v.size(); i++)
	{
		cout << v[i] << "";
	}
	cout << endl;
}
int main()
{
	/*vector_test1();*/
	test_vector3();
	return 0;
}

find?

vector_第11张图片

 我们发现,vector的成员函数并没有find,为什么呢?

我们在std标准库找到了find函数

 因为除了string容器之外,其他容器的find函数都是通过迭代器实现的,所以我们把这些容器的find函数直接写成一个,直接写在标准库中。

vector_第12张图片

 我们从迭代器first找到last,因为last是容器最后一个元素的下一个位置,所以last一定不是需要找的元素,所以当找到时,我们返回对应的迭代器,没有找到,我们返回last这个迭代器即可。

我们进行实验:

void test_vector3()
{
	vector v;
	v.resize(10);
	for (size_t i = 0; i < v.size(); i++)
	{
		v[i] = i;
	}
	vector::iterator it = find(v.begin(), v.end(), 3);
	if (it != v.end())
	{
		v.insert(it, 30);
	}
	for (auto e : v)
	{
		cout << e << " ";
	}
	cout << endl;
}
int main()
{
	/*vector_test1();*/
	test_vector3();
	return 0;
}

我们的目的是从v中找到3的位置,在3的位置插入10。

 问题:string为什么不使用标准库里面的find函数?

答:1:因为string出现的比较早,string出现的时候,标准库还没有完善。

2:因为string可能存在查找子串,我们用迭代器无法实现查找子串。

erase

表示删除vector的元素,分为两个接口函数,第一个接口函数是删除迭代器位置的一个元素,第二个接口函数是删除两个迭代器之间的元素。
具体的使用案例我们在网站就能查到,这里不再赘述。

swap

vector_第13张图片

我们知道,算法库里面也有一个swap函数:

vector_第14张图片

 这两个函数哪一个函数更建议使用?

答:我们更建议使用vector的成员函数swap,如图所示:

vector_第15张图片

 标准库里面的swap函数是通过类模板实现的,并且中间使用了三次拷贝构造函数,因为vector中的数据类型也可能是自定义类型,对于自定义类型的拷贝构造效率太低,所以我们尽量使用vector自己提供的swap函数。

 既然vector已经提供了swap函数,但是为什么标准库std又特殊的为vector提供了swap函数,如图所示:

vector_第16张图片

 我们写一串代码进行分析:

void test_vector3()
{
	vector v,v1;
	v1.push_back(10);
	v1.push_back(20);
	v1.push_back(30);
	v1.swap(v);
	swap(v1, v);
}
int main()
{
	/*vector_test1();*/
	test_vector3();
	return 0;
}

这里出现了两个swap函数,第一个swap函数是vector的成员函数,第二个swap函数是标准库std里面的swap函数。

 我们知道,算法库里面的swap函数走的是三次深拷贝,但是我们这里的第二个函数调用的就是算法库里面的swap,是否真的会调用三次深拷贝呢?

答:并不会,如图所示:

 我们知道,编译器在匹配摸板的时候,一定找最合适的进行匹配,因为我们已经写好了我们是vector的摸板,所以调用算法库里面的swap函数直接匹配这里的摸板,有了摸板就不需要多次深拷贝了。

简单的说,如图所示:

vector_第17张图片

 我们这里调用的swap(v1,v)会进行检查,编译器发现swap的两个参数都是vector,所以我们直接调用转换为调用vector的成员函数swap,也就是这样:v1.swap(v)

所以我们调用的这两个函数,最终的结果是一样的,第一个函数直接调用vector的成员函数swap,第二个调用标准库的swap函数,swap函数再进行识别,我们转而调用vector的成员函数swap。
 

shrink_to_fit

vector_第18张图片

表示把vector的容量调整到和vector的size相同。

提出一个问题:缩容适合使用吗?

不适合,原因如下:首先,缩容是异地缩容,如图所示:

vector_第19张图片

 我们的size是8,我们的capacity是16,假如我们要进行缩容,我们首先开辟一个空间大小为8的一份空间:

vector_第20张图片

 然后我们把v1的有效元素放到tmp上,然后让v1指向tmp,然后释放掉v1即可。

vector_第21张图片

这就是异地缩容的原理,所以异地缩容比较繁琐,效率低下,有没有原地缩容?

答:并没有原地缩容,例如:

 vector_第22张图片

 这是我们向内存申请的空间,假如我们想要释放空间的话,我们是不允许从middle位置释放空间的,因为这一部分空间是连贯的,我们只能从begin位置释放整个空间,所以也就不支持本地缩容。

vector函数支持缩容吗?

答:不支持,我们进行实验:

void test_vector3()
{
	vector v,v1;
	v1.reserve(10);
	v1.push_back(10);
	v1.push_back(20);
	v1.push_back(30);
	cout << v1.capacity() << endl;
	v1.reserve(3);
	cout << v1.capacity() << endl;
}
int main()
{
	/*vector_test1();*/
	test_vector3();
	return 0;
}

 假如vector支持缩容时,我们打印的结果应该为3.

所以vector并不支持缩容。

 resize可以缩容吗?

答:不可以,我们进行实验:

void test_vector3()
{
	vector v,v1;
	v1.reserve(10);
	v1.push_back(10);
	v1.push_back(20);
	v1.push_back(30);
	cout << v1.capacity() << endl;
	v1.resize(3);
	cout << v1.capacity() << endl;
}
int main()
{
	/*vector_test1();*/
	test_vector3();
	return 0;
}

我们进行运行:

vector_第23张图片

resize也不能缩容。

总结:vector一般不支持缩容。 

 在如今的计算机领域,硬件存储空间越来越大的情况下,时间就要比空间重要的多,所以我们一般不频繁使用缩容函数,因为缩容一定是异地缩容,异地缩容的本质是以时间换空间。

我们对shrink_to_fit进行实验:

void test_vector3()
{
	vector v,v1;
	v1.reserve(10);
	v1.push_back(10);
	v1.push_back(20);
	v1.push_back(30);
	cout << v1.capacity() << endl;
	v1.shrink_to_fit();
	cout << v1.capacity() << endl;
}
int main()
{
	/*vector_test1();*/
	test_vector3();
	return 0;
}

我们进行运行:

成功缩容。

 vector的模拟实现:

我们首先看一下vector的源代码,我们通过调试就可以看pj版本的源代码:

vector_第24张图片

 vector在pj版本下的源代码大概是3000行,这里的内容是开源声明。

sgi的版本更适合我们初学者学习,我们查看sgi版本。

我们把我们在源码中找最重要的代码,进行精简:

vector_第25张图片

 我们可以发现,vector的成员变量的雷西那个都是迭代器。

vector的结构图如图所示:

vector_第26张图片

 我们按照这个框架模拟实现vector

vector的框架:

#pragma once
namespace bit
{
	template
	clasee vector
	{
	public:
		typedef T* iterator;
	private:
		iterator _start;
		iterator _finish;
		iterator _endofstorge;
};
}

T是我们定义的摸板,我们把T*类型重命名为iterator,我们有三个成员变量。

构造函数:

 这是源代码中vector的构造函数,其中使用了初始化列表,把三个迭代器都置为0,我们按照他的写法实现构造函数。

vector()
			:_start(nullptr)
			, _finish(nullptr)
			, _endofstorge(nullptr)
		{}

我们的三个成员变量都是迭代器,我们可以先把迭代器理解为指针,对于指针的初始化,我们可以赋值nullptr,初始化列表是在调用函数内容之前就已经实现的。

size和capacity

size_t size()
		{
			return _finish - _start;
		}
		size_t capacity()
		{
			return _endofstorage - _start;
		}

我们把迭代器先理解为指针,指针与指针相减的结果为数字,size是有效元素的个数,capacity是容量,我们根据图像就能够写出对应的函数。

vector_第27张图片

 reserve函数:

void reserve(size_t n)
		{
			if (n > capacity())
			{
				T*tmp = new T*[n];
				memcpy(tmp, _start, sizeof(T)*size());
				delete[] _start;
				_start = tmp;
				_finish = _start + size();
				_endofstorge = _start + n;
			}
		}

我们进行画图解释:

 只有当n大于我们的capacity时,我们才进行扩容,我们的reserve不缩容。

我们的思路是异地扩容,我们新申请一个大小为n的空间tmp,然后把vector上的数据拷贝到tmp上,然后释放掉原来vector的数据,让新的空间指向tmp,再对_finish和endofstorge进行修改。

这里可以用malloc初始化吗?

答:不能,因为malloc只会对内置类型初始化,对于自定义类型不初始化。

new相较于malloc的优点:

malloc需要判断是否申请空间失败,而new不需要,new失败的话会抛异常。 

begin和end

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

[]

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

我们的[]函数内部必须要断言:要求pos一定要小于size()

尾插函数:

void push_back(const T&x)
		{
			if (_finish == _endofstorge)
			{
				size_t newcapacity = capacity() == 0 ? 4 : capacity() * 2;
				reserve(newcapacity);
			}
			*_finish = x;
			++_finish;
		}

我们进行尾插的数据类型 T既有可能是内置类型,也有可能是自定义类型,对于内置类型,我们可以不传引用,但是对于自定义类型必须传引用。

我们传了引用,在函数中可能会导致x的值发生变化,我们加上const表示x是只读的。

 vector_第28张图片

 假如我们的元素数和容量相等,表示我们需要扩容了,又因为capacity的初始化值可能为0,所以我们使用三目操作符,假如capacity为0时,我们把4赋给newCapacity,然后调用reserve函数进行扩容。

判断并执行扩容之后,接下来,进行尾插元素,我们的迭代器可以理解为指针 ,我们在_finish插入元素x,再++_finish即可。

特殊情况:

vector_第29张图片

 当我们的vector没有一个元素,我们进行尾插时,我们的newcapacity就被设置为4,然后我们调用reserve函数进行扩容:

vector_第30张图片

 这时候,因为我们vector中的元素实际为空,所以我们并不需要进行内存拷贝,所以我们可以优化一下reserve函数

void reserve(size_t n)
		{
			if (n > capacity())
			{
				T*tmp = new T*[n];
				if (_start)
				{
					memcpy(tmp, _start, sizeof(T)*size());
					delete[] _start;
				}
				_start = tmp;
				_finish = _start + size();
				_endofstorge = _start + n;
			}
		}

接下来,我们测试之前写的代码是否有误。

测试代码

using namespace std;
#include
#include"vector.h"
void test_vector1()
{
	bit::vector v;
	v.push_back(1);
	v.push_back(2);
	v.push_back(3);
	v.push_back(4);
	cout << v.capacity() << endl;
	cout << v.size() << endl;
}
int main()
{
	test_vector1();
	return 0;
}

vector_第31张图片

报错的原因在我们的reserve函数上:

 vector_第32张图片

 vector_第33张图片

我们画图进行解释:

 vector_第34张图片

 我们让_start指向tmp对应的空间:

vector_第35张图片

 这时候,我们的_start和_finish不在同一块空间内,我们调用size函数:

 两块不同的空间进行相加减的值是未知的,我们接下来会对_finsh进行解引用,就会报错。

我们可以这样处理:

 我们可以先处理finish,画图进行解释:

vector_第36张图片

我们先处理finish

 size函数表示_finish和_start相减,两个指针相减的结果是两个指针区域指向的相同数据的数目,是整型,我们让tmp+_finish.

vector_第37张图片

 这时候,我们再把tmp赋值给_start。

vector_第38张图片

这时候就没有什么问题了,我们继续测试:

 vector_第39张图片

 const修饰的begin和end

vector_第40张图片

我们发现,begin和end都有两个接口函数,分别是const和非const。

 

 这两个const有什么区别吗?

答:第二个const:假如调用begin()函数的对象用const修饰了,我们就调用这个begin()函数。

第一个const:我们用const对象调用begin函数,返回的迭代器也用const修饰。

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

const修饰的[]

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

empty函数:

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

当_start和_finsh相等时,也就是当_start=_finsh=nullptr时,vector对象为空。

resize:

vector_第41张图片

void resize(size_t n, T val = T())
		{
			if (n>capacity())
			{
				reserve(n);
			}
			if (n > size())
			{
				while (_finish < _start + n)
				{
					*_finish = val;
					++_finish;
				}
			}
			else
			{
				_finish = _start + n;
			}
		}

 

这里的缺省值可以写0吗?

答:不行,对于内置类型,我们可以用0初始化,但是vector存储的数据既有可能是内置类型,也有可能是自定义类型,而对于自定义类型,我们不能用0去初始化。

 vector_第42张图片

 我们首先判断n是否大于capacity,如果大于我们需要进行扩容。

vector_第43张图片

 判断n是否大于size,大于时需要填元素,我们调用循环把val填入到vector中。

vector_第44张图片

 这里表示n小于size,所以我们需要元素,我们可以直接修改_finish的值,就能达到删除数据的目的。

尾删

void pop_back()
		{
			_finish--;
		}

我们直接让_finish--来删除数据。

不断的尾删会导致什么样的结果:

using namespace std;
#include
#include
#include"vector.h"
void test_vector1()
{
	bit::vector v;
	v.push_back(1);
	v.push_back(2);
	v.push_back(3);
	v.push_back(4);
	while (1)
	{
		v.pop_back();
		cout << v.size() << endl;
		Sleep(100);
	}
}
int main()
{
	test_vector1();
	return 0;
}

我们进行运行:

vector_第45张图片

 因为我们的size是无符号类型,所以不断的进行尾删之后,size反而变成一个非常大的数据了。

我们调用库里面的尾删函数进行尝试:

using namespace std;
#include
#include
#include
//#include"vector.h"
void test_vector1()
{
	vector v;
	v.push_back(1);
	v.push_back(2);
	v.push_back(3);
	v.push_back(4);
	while (1)
	{
		v.pop_back();
		cout << v.size() << endl;
		Sleep(100);
	}
}
int main()
{
	test_vector1();
	return 0;
}

vector_第46张图片

我们发现,库里面报的错误是断言错误,所以我们可以仿照库的写法对尾删函数进行完善:

 

void pop_back()
		{
			assert(_finish >= _start);
			_finish--;
		}

insert:

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

insert表示我们从pos位置处插入元素val。

为什么这里要加上const和&?

答:首先,必须加上引用,因为我们insert插入的极有可能是内置类型,也有可能是自定义类型,传参的过程本质是一个拷贝,对于自定义类型的深拷贝效率太低,消耗很大,所以我们通过传引用来传参,传引用的话我们就要限制函数内部,禁止对val进行修改,所以要加上const。

vector_第47张图片 

首先,我们要判断是否需要扩容。

 vector_第48张图片

我们的思路是这样的,先把pos位置后的数据全部往后面挪动一个位置,然后才填数据。 

 我们对insert函数进行实验:

using namespace std;
//#include
#include
#include
#include
#include"vector.h"
void test_vector1()
{
	bit::vector v;
	v.push_back(1);
	v.push_back(2);
	v.push_back(3);
	v.push_back(4);
	bit::vector::iterator it = v.end();
	v.insert(it, 15);
	for (auto s : v)
	{
		cout << s << " ";
	}
	cout << endl;
}
int main()
{
	test_vector1();
	return 0;
}

我们在调试时发现两处异常:

异常1:vector_第49张图片

我们发现,编译器会一直执行while循环,不会终止。

异常2:

 

 我们进行的是尾插,pos与_finish的值却不相等。

这些问题是扩容的问题:

扩容导致了迭代器失效:

vector_第50张图片

 因为我们只有我们的capacity和size都为4,我们要插入元素就需要扩容,我们的pos的值和_finish是相等的。

 vector_第51张图片

 我们的原空间被销毁,所以pos迭代器就失效了,pos和_finish无法比较大小,所以while死循环,pos位置已经被释放了所以无法被继续访问。

我们要做的就是更新pos:

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

我们思考一个问题:insert之后的迭代器还能继续使用吗?

如代码所示:

 vector_第52张图片

答:最好不要,首先,只要我们因为调用insert而导致扩容,就会导致迭代器失效,it位置已经被释放。

许多人会有疑问:我们不是已经解决了迭代器失效了吗?

vector_第53张图片 

我们只是保证函数能正常使用,但是并没有保证迭代器没有失效:如图:

 

我们调用insert传参传递的it是传值传参,pos只是it的拷贝,pos值的修改并不影响it的变化,所以迭代器it还是失效了 。

那为什么insert函数不传引用传递第一个参数呢?

如图:

为什么不这样写呢?

 答:因为限制条件太多 ,例如:

 假如我们这样调用函数时,因为我们是传引用,我们的v.begin()是调用begin函数的返回值,是一个临时变量,临时变量具有常数属性,我们无法把临时变量当作参数调用insert函数。

所以,调用insert(pos,val)之后,pos对应的迭代器最好不要再使用。

标准库里面的find函数:

void test_vector1()
{
	bit::vector v;
	v.push_back(1);
	v.push_back(2);
	v.push_back(3);
	v.push_back(4);
	bit::vector::iterator it = find(v.begin(), v.end(), 3);
	if (it != v.end())
	{
		v.insert(it, 30);
	}
	for (auto s : v)
	{
		cout << s << " ";
	}
	cout << endl;
}

我们进行运行:

 

你可能感兴趣的:(算法,c++,开发语言)