【C++】vector的使用


在之前我们学习了string的使用与模拟实现,在参考文档中可以发现,vector和string以及其他的容器的接口基本都相同,所以这里也就简要的介绍一下。

同样的,在这里还是要说一下,对于STL库的学习,不可能在一朝一夕之间学完,要学会去查看文档和多练习vector参考文档

1. 主要结构

image-20230418154326704

vector没有像string一样进行类型重定义typedef basic_string<char> string,所以在使用vector的时候要指定模板参数类型例如vector<int>

2. 构造函数与复制重载

image-20230418160501765

可以看到vector的构造函数实现相较于string就简化了很多,一共分为四个,分别是默认构造,指定长度和值的构造,迭代器区间构造,拷贝构造

void Test_Construct()
{
    
    
	vector<int> v1;//默认构造
	vector<int> v2(10, 3);//指定长度和值的构造
	string s("aaaaaaaaaaaaaa");
	vector<char> v3(s.begin(), s.end());//迭代器区间构造
	vector<char> v4(v3);//拷贝构造
}

image-20230418213255290

通过监视查看四个对象的值如上。

3. 迭代器

由于vector也是通过动态数组实现的,所以原生指针就能很好的支持迭代器行为,所以在vector中的迭代器本质也是指针。

image-20230419080950623

可以看到和string一样,vector的迭代器也分为普通迭代器,反向迭代器,const迭代器,const反向迭代器。

image-20230419081331230

对于迭代器和反向迭代器的工作方式如上图。

4. 容量相关

1.容量读取

1. size

image-20230419081458467

返回一个size_t类型的vector中数据个数

2. capacity

image-20230419081623104

返回一个size_t类型的vector的容量。

使用范例:

void Test_Capacity()
{
    
    
	vector<int> v;
	v.reserve(10);
	for (size_t i = 0; i < 5; ++i)
	{
    
    
		v.push_back(i);
	}
	cout << "size:" << v.size() << endl;
	cout << "capacity:" << v.capacity() << endl;
}

运行结果为:

image-20230419082018970

2.容量修改

1. reserve

image-20230419082143747

扩大vector的容量到n,如果原来vector的容量大于等于n,不做任何操作

void Test_Capacity2()
{
    
    
	vector<int> v(5, 1);
	cout << "before reserve capacity:" << v.capacity() << endl;
	v.reserve(10);
	cout << "after reserve capacity:" << v.capacity() << endl;
}

image-20230419082505632

2. resize

image-20230419082619237

修改vector中的数据个数,如果n小于size,那就修改size;如果n大于size但是小于capacity,那就尾插数据直到size==n,如果n大于capacity,那就先扩容到n,再填充数据。

void Test_Capacity3()
{
    
    
	vector<int> v(5, 1);
	cout << "before resize capacity:" << v.size() << endl;
	v.resize(10);
	cout << "after resize capacity:" << v.size() << endl;
	for (auto e : v)
	{
    
    
		cout << e << " ";
	}
	cout << endl;
}

image-20230419083020574

5. 数据访问

与string类似,在数据访问中也有很多接口,但是最常用的还是[]的重载,其余基本不常用,这里我们就不过多赘述

image-20230419083205192

[]实际上是一个运算符重载,使vector也具有类似下标访问的能力,返回值是vector中存放元素类型的引用

对于[]的重载,在库里面支持了两种重载,分别是普通类型的和const类型的用于支持多样化的使用环境

怎么判断需要实现const或者非const版本?

  1. 对于只读接口,只实现const版本即可
  2. 对于只写接口,只实现非const版本即可
  3. 对于可读可写的接口,需要同时实现const版本和非const版本
void Test_Access()
{
    
    
	vector<int> v(10);
	for (size_t i = 0; i < v.size(); ++i)
	{
    
    
		v[i] = i;
	}
	for (size_t i = 0; i < v.size(); ++i)
	{
    
    
		cout << v[i] << " ";
	}
	cout << endl;
}

image-20230419083716301

6. 数据修改

1. 尾插尾删

1. push_back

image-20230419085534437

尾插数据,插入前会进行扩容操作,每次扩容的大小取决于编译器的实现

2. pop_back

image-20230419085728462

如果vector中有数据的话,尾删一个数据,否则会由assert中断程序或者抛出异常,具体情况取决于编译器的实现

void Test_Modifity()
{
    
    
	vector<int> v;
	v.push_back(1);
	v.push_back(2);
	v.push_back(3);
	v.push_back(4);
	v.push_back(5);
	v.pop_back();
	v.pop_back();
	v.pop_back();
	v.pop_back();
	v.pop_back();
	v.pop_back();
}

image-20230419090506991

2.任意位置的插入删除

1. insert

image-20230419090827948

在传入的任意位置插入一个数据或者迭代器区间的数据

2. erase

image-20230419091215741

删除任意位置的值或者任意一个迭代器区间的值

void Test_Modifity2()
{
    
    
	vector<int> v;
	v.push_back(1);
	v.push_back(2);
	v.push_back(3);
	v.push_back(4);
	v.push_back(5);
	v.insert(v.begin() + 3, 30);
	v.erase(v.begin());
    for (auto e : v)
	{
    
    
		cout << e << " ";
	}
	cout << endl;
}

image-20230419092501957

这里注意一下,vector中会出现迭代器失效的问题

❗❗==vector中的迭代器失效问题==❗❗

第一个问题:迭代器的出现原因是什么?

迭代器的设计模式,为了不暴露底层实现细节,提供统一的方式来访问容器,对于vector,其迭代器本质上就是原生指针T*,所以对vector来说,迭代器失效的原因就是原迭代器指向的空间被销毁,导致出现野指针。

第二个问题:导致迭代器失效的操作有哪些?

✅迭代器失效的原理是底层空间改变,导致产生野指针,所以所有的底层空间的改变都有可能使迭代器失效,例如:reserve,resize,push_back,insert等。

✅除了对底层空间的改变之外,erase也会导致迭代器失效。

第三个问题:按照我们上一个问题的逻辑,erase没有使底层空间发生改变啊,为什么也会使迭代器失效?

✅虽然erase没有改变底层空间,但是,如果erase删除的是vector中最后一个元素,刚好删除完之后pos位置是end指向的位置,那么迭代器就失效了。

✅就算删除的不是最后一个元素,那么迭代器指向的位置的值也是改变了的,严格意义上来说,也是不能实现我们所需要的功能的,所以也可以认为是迭代器失效了。

第四个问题:只有vector会遇到迭代器失效的的情况吗,string有没有迭代器失效的情况?

✅不只是vector有,string和其他的容器都有迭代器失效的情况。

第五个问题:迭代器失效的问题怎么解决呢?

✅在上文中,我们讲解了vector的主要接口,可以注意到,能够引起迭代器失效的接口函数的返回值都是迭代器,这里返回的迭代器就是我们所需要的有效的迭代器。所以,在使用前对迭代器进行重新赋值即可解决

//关于迭代器失效解决的例题:
//对于使用vector存储的数组{0,1,2,3,4,5,6,7,8,9},删除其中所有的偶数元素
int main()
{
     
     
	vector<int> v(10);
	for (size_t i = 0; i < 10; ++i)
	{
     
     
		v[i] = i;
	}
	vector<int>::iterator 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;
	return 0;
}

7.其他接口

1. clear

image-20230419092648474

删除vector内部所有数据(本质上做的操作就是将vector的size置为0)

2. swap

image-20230419092900368

vector内部实现的一个交换函数,提升效率。

猜你喜欢

转载自blog.csdn.net/weixin_63249832/article/details/130238459