STL--vector

目录

  • 一、vector的介绍及应用
    • 1、vector的介绍
    • 2、vector的应用
      • 1.2.1(constructor)构造函数声明
      • 1.2.2 vector iterator迭代器的使用
      • 1.2.3 vector 空间增长问题
      • 1.2.3 vector 增删查改
      • 1.2.4 vector 迭代器失效问题。
  • 二、vector深度剖析及模拟实现(稍后更新)

一、vector的介绍及应用

1、vector的介绍

vector是STL(标准模板库)中最常见的容器,它是一种顺序容器,支持随机访问。vector是一块连续分配的内存,从数据安排的角度来讲,和数组极其相似,不同的地方就是:数组是静态分配空间,一旦分配了空间的大小,就不可再改变了;而vector是动态分配空间,随着元素的不断插入,它会按照自身的一套机制不断扩充自身的容量。

vector的扩充机制:按照容器现在容量的一倍进行增长。vector容器分配的是一块连续的内存空间,每次容器的增长,并不是在原有连续的内存空间后再进行简单的叠加,而是重新申请一块更大的新内存,并把现有容器中的元素逐个复制过去,然后销毁旧的内存。这时原有指向旧内存空间的迭代器已经失效,所以当操作容器时,迭代器要及时更新。

2、vector的应用

1.2.1(constructor)构造函数声明

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

using namespace std;
int main() {
     
	vector<int> sdt;//无参构造
	vector<int>  s1(4,100);//构造并初始化4个100
	vector<int> s2(s1.begin(), s1.end());//使用迭代器进行初始化构造
	vector<int> s3(s1);//拷贝构造
	return 0;
}

STL--vector_第1张图片
用s1拷贝给s2和s3它们的有效位为4位

STL--vector_第2张图片
把s1的内容拷贝给s2和s3它们的有效位为4位
std采用无参构造所以没有数据

1.2.2 vector iterator迭代器的使用

begin + end        获取第一个数据位置的iterator/const_iterator,
                   获取最后一个数据的下一个位置的
                   iterator/const_iterator
rbegin + rend      获取最后一个数据位置的reverse_iterator,、
                   获取第一个数据前一个位置的reverse_iterator

STL--vector_第3张图片

#include
#include 

using namespace std;
int main() {
     
	vector<int> sdt;//无参构造
	vector<int>  s1(2,4);//构造并初始化4个100
	//vector s2(s1.begin(), s1.end());//使用迭代器进行初始化构造
	//vector s3(s1);//拷贝构造
	// const对象使用const迭代器进行遍历打印
	s1.push_back(1);
	s1.push_back(2);
	s1.push_back(3);
	s1.push_back(4);
//使用迭代器进行遍历打印
	vector<int>::iterator it = s1.begin();
	while (it != s1.end())
	{
     
		cout << *it << " ";
		++it;
	}
	cout << endl;
	//反向迭代器进行遍历打印
	vector<int>::reverse_iterator it1 = s1.rbegin();
	while (it1 != s1.rend())
	{
     
		cout << *it1 << " ";
		++it1;
	}
	cout << endl;
	return 0;
}

这里的push_back函数功能和string中的功能一样都是在尾部插入一个数据
在这里插入图片描述

1.2.3 vector 空间增长问题

size                   获取数据个数
capacity               获取容量大小
empty                  判断是否为空
resize                 改变vector的size
reserve                改变vector放入capacity

capacity的代码在vs和g++下分别运行会发现,vs下capacity是按1.5倍增长的,g++是按2倍增长的。
这个问题经常会考察,不要固化的认为,顺序表增容都是2倍,具体增长多少是根据具体的需求定义
的。vs是PJ版本STL,g++是SGI版本STL。
reserve只负责开辟空间,如果确定知道需要用多少空间,reserve可以缓解vector增容的代价缺陷问
题。
resize在开空间的同时还会进行初始化,影响size

// vector::capacity
#include 
#include 
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';
		}
	}
}

STL--vector_第4张图片

#include
#include 

using namespace std;
int main() {
     
	vector<int> sdt;//无参构造
	cout <<"1为空:"<<sdt.empty() << endl;//判断sdt是否为空
	vector<int>  s1(1, 1);//构造并初始化4个100
	cout << s1.size() << endl;
	cout << s1.capacity() << endl;
	s1.push_back(1);
	s1.push_back(2);
	s1.push_back(3);
	s1.push_back(4);
	//size                   获取数据个数
	//	capacity               获取容量大小
	//	empty                  判断是否为空
	//	resize                 改变vector的size
	//	reserve                改变vector放入capacity
	cout << s1.size() << endl;
	cout << s1.capacity() << endl;
	s1.resize(10);//把s1有效位扩至10没数据的用0代替
	cout << "把s1有效位扩至10没数据的用0代替" << endl;
	cout << s1.size() << endl;
	cout << s1.capacity() << endl;
	vector<int>::iterator it = s1.begin();
	while (it != s1.end())
	{
     
		cout << *it << " ";
		++it;
	}
	cout << endl;
	cout << "把s1有效位扩至15没数据的用5代替" << endl;
	s1.resize(15,5);//把s1有效位扩至15没数据的用5代替
	cout << s1.size() << endl;
	cout << s1.capacity() << endl;
	cout << "//把s1的空间长度扩至100" << endl;
	s1.reserve(100); //把s1的空间长度扩至10
	cout << s1.size() << endl;
	cout << s1.capacity() << endl;
	vector<int>::iterator it1 = s1.begin();
	while (it1!= s1.end())
	{
     
		cout << *it1 << " ";
		++it1;
	}
	cout << endl;

	return 0;
}

STL--vector_第5张图片

1.2.3 vector 增删查改

push_back            尾插
pop_back             尾删
find 查找。          (注意这个是算法模块实现,不是vector的成员接口)
insert               在position之前插入val
erase                删除position位置的数据
swap                 交换两个vector的数据空间
operator[]           像数组一样访问
#include
#include 

using namespace std;
int main() {
     
	vector<int> sdt;//无参构造
	vector<int>  s1(1, 1);//构造并初始化4个100
	s1.push_back(1);
	s1.push_back(2);
	s1.push_back(3);
	s1.push_back(4);
	vector<int>::iterator it = s1.begin();
	while (it != s1.end())
	{
     
		cout << *it<< " ";
		++it;
	}
	cout << endl;
	s1.pop_back();
	vector<int>::iterator it1 = s1.begin();
	while (it1 != s1.end())
	{
     
		cout << *it1 << " ";
		++it1;
	}
	cout << endl;
	// 使用find查找3所在位置的iterator
	vector<int>::iterator pos = find(s1.begin(), s1.end(), 3);

	// 在pos位置之前插入44
	s1.insert(pos, 44);
	vector<int>::iterator it2 = s1.begin();
	while (it2 != s1.end()) {
     
		cout << *it2 << " ";
		++it2;
	}
	cout << endl;
	//找到3的位置
	vector<int>::iterator pos1= find(s1.begin(), s1.end(), 3);
	//删除之前插入到之前的44
	s1.erase(pos1 -1);
	vector<int>::iterator it3 = s1.begin();
	while (it3 != s1.end()) {
     
		cout << *it3 << " ";
		++it3;
	}



	return 0;
}

STL--vector_第6张图片

#include
#include 

using namespace std;
int main() {
     
	vector<int> sdt;//无参构造
	vector<int>  s1(1, 1);//构造并初始化4个100
	s1.push_back(1);
	s1.push_back(2);
	s1.push_back(3);
	s1.push_back(4);
	cout << s1[0] << endl;
	s1[0] = 4;//通过[]改变第1个值
	cout << s1[0] << endl;
	//通过[]遍历
	for (int i = 0; i < s1.size(); i++) {
     
		cout << s1[i] <<" ";
	}
	cout << endl;
	//s1首部插入4
	s1.insert(s1.begin(), 4);
	for (int i = 0; i < s1.size(); i++) {
     
		cout << s1[i] << " ";
	}
	cout << endl;
	sdt.swap(s1);
	cout << "sdt:";
	for (int i = 0; i < sdt.size(); i++) {
     
		cout << sdt[i] << " ";
	}
	cout << endl;
	cout << "s1:";
	for (int i = 0; i < s1.size(); i++) {
     
		cout << s1[i] << " ";
	}
	cout << endl;
	// 范围for
	for (auto x: sdt) {
     
		cout << x << " ";
	}
	cout << endl;	return 0;
}

STL--vector_第7张图片

标记的地方 交换前sdt为空 s1和sdt换了工作空间s1为空

1.2.4 vector 迭代器失效问题。

#include
#include 

using namespace std;
int main() {
     
	vector<int> sdt;//无参构造
	vector<int>  s1(1, 1);//构造并初始化4个100
	s1.push_back(1);
	s1.push_back(2);
	s1.push_back(3);
	s1.push_back(4);
	vector<int>::iterator it = s1.begin();
	// 使用find查找3所在位置的iterator
	vector<int>::iterator pos = find(s1.begin(), s1.end(), 3);
	// 在pos位置之前插入44
	s1.insert(pos, 44);
	vector<int>::iterator it2 = s1.begin();
	while (it2 != s1.end()) {
     
		cout << *it2 << " ";
		++it2;
	}
	cout << endl;

	//删除之前插入到之前的44
	s1.erase(pos - 1);

	return 0;
}

说明:s1.insert(pos, 44);在插入是会因为空间不够会增容,增容之后pos会失效,之后的s1.erase(pos - 1);操作会出错,所以整个程序会奔溃。
迭代器的主要作用就是让算法能够不用关心底层数据结构,其底层实际就是一个指针,或者是对指针进行了封装,比如:vector的迭代器就是原生态指针T。因此迭代器失效,实际就是迭代器底层对应指针所指向的空间被销毁了,而使用一块已经被释放的空间,造成的后果是程序崩溃(即如果继续使用已经失效的迭代器,程序可能会崩溃)。*
**对于vector可能会导致其迭代器失效的操作有:

  1. 会引起其底层空间改变的操作,都有可能是迭代器失效,比如:resize、reserve、insert、assign、push_back等。**
#include 
using namespace std;
#include 
int main()
{
     
 vector<int> v{
     1,2,3,4,5,6};
 
 auto it = v.begin();
 
 // 将有效元素个数增加到100个,多出的位置使用8填充,操作期间底层会扩容
 // v.resize(100, 8);
 
 // reserve的作用就是改变扩容大小但不改变有效元素个数,操作期间可能会引起底层容量改变
 // v.reserve(100);
 
 // 插入元素期间,可能会引起扩容,而导致原空间被释放
 // v.insert(v.begin(), 0);
  // v.push_back(8);
 
 // 给vector重新赋值,可能会引起底层容量改变
 v.assign(100, 8);
 
 /*

 */
 while(it != v.end())
 {
     
 cout<< *it << " " ;
 ++it;
 }
 cout<<endl;
 return 0;
}

说明: 出错原因:以上操作,都有可能会导致vector扩容,也就是说vector底层原理旧空间被释放掉,而在打印时,it还使用的是释放之间的旧空间,在对it迭代器操作时,实际操作的是一块已经被释放的空间,而引起代码运行时崩溃。
解决方式:在以上操作完成之后,如果想要继续通过迭代器操作vector中的元素,只需给it重新赋值即可。

下边代码重新赋值就解决了迭代器失效的问题

#include
#include 

using namespace std;
int main() {
     
	vector<int> sdt;//无参构造
	vector<int>  s1(1, 1);//构造并初始化4个100
	s1.push_back(1);
	s1.push_back(2);
	s1.push_back(3);
	s1.push_back(4);
	vector<int>::iterator it = s1.begin();
	// 使用find查找3所在位置的iterator
	vector<int>::iterator pos = find(s1.begin(), s1.end(), 3);
	// 在pos位置之前插入44
	s1.insert(pos, 44);
	vector<int>::iterator it2 = s1.begin();
	while (it2 != s1.end()) {
     
		cout << *it2 << " ";
		++it2;
	}
	cout << endl;
	pos迭代器失效要重新找到3的位置
	找到3的位置
	vector<int>::iterator pos1 = find(s1.begin(), s1.end(), 3);
	//修改地方
	//删除之前插入到之前的44
	s1.erase(pos1 - 1);
	vector<int>::iterator it3 = s1.begin();
	while (it3 != s1.end()) {
     
		cout << *it3 << " ";
		++it3;
	}
	return 0;
}
#include 
using namespace std;
#include 
int main()
{
     
	int a[] = {
      1, 2, 3, 4 };
	vector<int> v(a, a + sizeof(a) / sizeof(int));
	// 使用find查找3所在位置的iterator
	vector<int>::iterator pos = find(v.begin(), v.end(), 3);
	// 删除pos位置的数据,导致pos迭代器失效。
	v.erase(pos);
	cout << *pos << endl; // 此处会导致非法访问
	return 0;
}

erase删除pos位置元素后,pos位置之后的元素会往前搬移,没有导致底层空间的改变,理论上讲迭代
器不应该会失效,但是:如果pos刚好是最后一个元素,删完之后pos刚好是end的位置,而end位置是
没有元素的,那么pos就失效了。因此删除vector中任意位置上元素时,vs就认为该位置迭代器失效
了。

以下代码的功能是删除vector中所有的偶数

#include 
using namespace std;
#include 
int main()
{
     
	vector<int> v{
      1, 2, 3, 4 };
	auto it = v.begin();
	while (it != v.end())
	{
     
		if (*it % 2 == 0)
			v.erase(it);
		++it;
	}

	return 0;
}

这个程序删除vector中的偶数依次遍历当删除2后it会指向3自增指向4但是删除4后会向后挪移而4已经是end会访问不到而出错,而且要是两个偶数放在一起会跳过第二个偶数

int main()
{
     
	vector<int> v{
      1, 2, 3, 4 };
	auto it = v.begin();
	while (it != v.end())
	{
     
		if (*it % 2 == 0)
			it = v.erase(it);
		else
			++it;
	}
	return 0;
}

这是改良版的删除之后把挪动的位置位置赋给it,而且这个程序也解决了删除后还自增的问题,两个偶数放在一起还会被删除

二、vector深度剖析及模拟实现(稍后更新)

你可能感兴趣的:(c++)