【C++】vector常见用法

孤寂大仙v 2024-09-14 09:35:01 阅读 64

🔥个人主页🔥:孤寂大仙V

🌈收录专栏🌈:C++从小白到高手

🌹往期回顾🌹:[C++]string类

🔖 流水不争,争的是滔滔不息。

文章目录

一、vector的介绍vector的主要特点

二、vector的使用vector的定义vector迭代器的使用vector的空间增长问题vector的增删查改

三、vector迭代器失效问题对于vector可能导致迭代器失效的原因


一、vector的介绍

在C++中,vector是标准模板库(STL)中的一种动态数组容器,它允许存储同一类型的元素,并能自动调整大小。这与普通数组不同,vector的大小是可以动态改变的,即可以根据需要自动扩展或缩小。

vector的主要特点

动态大小:不像数组需要固定的大小,vector可以动态增加或减少元素个数。当新元素被添加时,它会自动调整底层存储容量。顺序容器:vector中的元素按插入顺序存储,支持通过下标访问和修改元素,类似于数组。自动管理内存:vector会自动管理内存分配和释放,因此用户不必担心手动分配内存,避免了使用普通数组时可能出现的内存泄漏问题。灵活的插入和删除操作:vector可以在末尾进行高效的插入和删除操作。虽然在中间插入和删除元素的效率较低,但相比普通数组仍有较多的灵活性。

二、vector的使用

vector的定义

构造函数声明

在这里插入图片描述

在这里插入图片描述

<code>vector<int> v1;//无参构造

在这里插入图片描述

<code>vector<int> v2(10,1);//构造并初始化10个1

在这里插入图片描述

<code>vector<int> v3(10, 2);

vector<int> v4 = v3;//拷贝构造

在这里插入图片描述

<code>vector<int> v4(10, 3);

vector<int> v5(v4.begin(), v4.end());

paint_vector(v5);//使用迭代器进行初始化构造

在这里插入图片描述

vector迭代器的使用

在这里插入图片描述

在这里插入图片描述

<code>void paint_vector2(vector<int>& v)

{

auto it = v.begin();

while (it != v.end())

{

cout << *it << " ";

++it;

}

cout << endl;

}

void test5()

{

vector<int> v5;

v5.push_back(1);

v5.push_back(2);

v5.push_back(3);

v5.push_back(4);

v5.push_back(5);

paint_vector2(v5);

}

int main()

{

test5();

return 0;

}

用vector创建一个容器,在里面放五个整形元素。打印的话用迭代器打印。begin()是获取第一个位置的数据元素,end()是获取最后一个位置的下一个位置的数据元素。

在这里插入图片描述

<code>void paint_vector3(vector<int>& v)

{

auto it = v.rbegin();

while (it != v.rend())

{

cout << *it << " ";

++it;

}

cout << endl;

}

void test6()

{

vector<int> v6;

v6.push_back(1);

v6.push_back(2);

v6.push_back(3);

v6.push_back(4);

v6.push_back(5);

paint_vector3(v6);

}

int main()

{

test6();

return 0;

}

rbegin正好反了过来,rbegin获取最后一个位置,rend获取第一个位置的前一个位置。

在这里插入图片描述

vector的空间增长问题

在这里插入图片描述

在这里插入图片描述

<code>vector<int> v7(10, 7);

cout << size(v7) << endl;//size获取数据个数

在这里插入图片描述

<code>vector<int> v8(10, 8);

cout << v8.capacity() << endl;//获取容量大小

在这里插入图片描述

<code>vector<int> v10(10, 10);

v10.resize(15, 5);//改变vector的size并且可以为增加的size的改变值

paint_vector(v10);

在这里插入图片描述

<code>vector<int> v11(10, 11);

v11.reserve(15);//一开始的capacity是10,用reserve改变capacity的容量

一开始的的capacity是10

在这里插入图片描述

经过reserve改变容量后capacity变为了15

在这里插入图片描述

capacity的代码在vs和g++下分别运行会发现,vs下capacity是按1.5倍增长的,g++是按2

倍增长的。这个问题经常会考察,不要固化的认为,vector增容都是2倍,具体增长多少是

根据具体的需求定义的。vs是PJ版本STL,g++是SGI版本STL。

reserve只负责开辟空间,如果确定知道需要用多少空间,reserve可以缓解vector增容的代

价缺陷问题。

resize在开空间的同时还会进行初始化,影响size。

vector的增删查改

在这里插入图片描述

在这里插入图片描述

<code>vector<int> v12;

v12.push_back(1);

v12.push_back(2);//尾插

paint_vector(v12);

在这里插入图片描述

<code>vector<int> v13(10,1);

v13.pop_back();

v13.pop_back();//尾删

paint_vector(v13);

在这里插入图片描述

<code>void test14()

{

vector<int> v14;

v14.push_back(1);

v14.push_back(2);

v14.push_back(3);

v14.push_back(4);

v14.push_back(5);

v14.push_back(6);

auto it = find(v14.begin(), v14.end(), 5);

if (it != v14.end())

{

cout << *it << " ";

++it;

}

else

{

cout << "no" << " ";

}

}

int main()

{

test14();

return 0;

}

如果在vector中可以找到val,就返回我们要找的值,如果vector中没有val,返回no。

在这里插入图片描述

<code>void test15()

{

vector<int> v15(10,1);

v15.insert(v15.begin()+3, 2);//在pos位置之前插入val。

paint_vector(v15);

}

int main()

{

test15();

return 0;

}

在这里插入图片描述

<code>void test16()

{

vector<int> v16;

v16.push_back(1);

v16.push_back(2);

v16.push_back(3);

v16.push_back(4);

v16.push_back(5);

v16.erase(v16.begin() + 3);//删除pos位置的数据

paint_vector(v16);

}

int main()

{

test16();

return 0;

}

在这里插入图片描述

<code>void test17()

{

vector<int> v17(5, 3);

vector<int> v18(5, 6);

paint_vector(v17);

paint_vector(v18);

swap(v17, v18);//交换两个vetor的数据空间。

paint_vector(v18);

paint_vector(v17);

}

int main()

{

test17();

return 0;

}

在这里插入图片描述

<code>void test18()

{

vector<int> v18;

v18.push_back(1);

v18.push_back(2);

v18.push_back(3);

v18.push_back(4);

v18.push_back(5);

cout<<v18[1]<<endl;

}

int main()

{

test18();

return 0;

}

在这里插入图片描述

三、vector迭代器失效问题

迭代器的主要作用就是让算法能够不用关心底层数据结构,其底层实际就是一个指针,或者是对

指针进行了封装,比如:vector的迭代器就是原生态指针T* 。因此迭代器失效,实际就是迭代器

底层对应指针所指向的空间被销毁了,而使用一块已经被释放的空间,造成的后果是程序崩溃(即

如果继续使用已经失效的迭代器,程序可能会崩溃)。

对于vector可能导致迭代器失效的原因

1.会引起其底层空间改变的操作,都有可能是迭代器失效,比如:resize、reserve、insert、

assign、push_back等。

<code>#include <iostream>

using namespace std;

#include <vector>

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);

/*

出错原因:以上操作,都有可能会导致vector扩容,也就是说vector底层原理旧空间被释

放掉,而在打印时,it还使用的是释放之间的旧空间,在对it迭代器操作时,实际操作的是一块

已经被释放的空间,而引起代码运行时崩溃。

解决方式:在以上操作完成之后,如果想要继续通过迭代器操作vector中的元素,只需给

it重新赋值即可。

*/

while(it != v.end())

{

cout<< *it << " " ;

++it;

}

cout<<endl;

return 0;

}

. 指定位置元素的删除操作–erase

#include <iostream>

using namespace std;

#include <vector>

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就认为该位置迭代器失效了。

注意:Linux下,g++编译器对迭代器失效的检测并不是非常严格,处理也没有vs下极端。

// 1. 扩容之后,迭代器已经失效了,程序虽然可以运行,但是运行结果已经不对了

int main()

{

vector<int> v{ 1,2,3,4,5};

for(size_t i = 0; i < v.size(); ++i)

cout << v[i] << " ";

cout << endl;

auto it = v.begin();

cout << "扩容之前,vector的容量为: " << v.capacity() << endl;

// 通过reserve将底层空间设置为100,目的是为了让vector的迭代器失效

v.reserve(100);

cout << "扩容之后,vector的容量为: " << v.capacity() << endl;

// 经过上述reserve之后,it迭代器肯定会失效,在vs下程序就直接崩溃了,但是linux

下不会

// 虽然可能运行,但是输出的结果是不对的

while(it != v.end())

{

cout << *it << " ";

++it;

}

cout << endl;

return 0;

}

程序输出:

1 2 3 4 5

扩容之前,vector的容量为: 5

扩容之后,vector的容量为: 100

0 2 3 4 5 409 1 2 3 4 5

// 2. erase删除任意位置代码后,linux下迭代器并没有失效

// 因为空间还是原来的空间,后序元素往前搬移了,it的位置还是有效的

#include <vector>

#include <algorithm>

int main()

{

vector<int> v{ 1,2,3,4,5};

vector<int>::iterator it = find(v.begin(), v.end(), 3);

v.erase(it);

cout << *it << endl;

while(it != v.end())

{

cout << *it << " ";

++it;

}

cout << endl;

return 0;

}

程序可以正常运行,并打印:

4

4 5

// 3: erase删除的迭代器如果是最后一个元素,删除之后it已经超过end

// 此时迭代器是无效的,++it导致程序崩溃

int main()

{

vector<int> v{ 1,2,3,4,5};

// vector<int> v{1,2,3,4,5,6};

auto it = v.begin();

while(it != v.end())

{

if(*it % 2 == 0)

v.erase(it);

++it;

}

for(auto e : v)

cout << e << " ";

cout << endl;

return 0;

}

========================================================

// 使用第一组数据时,程序可以运行

[sly@VM-0-3-centos 20220114]$ g++ testVector.cpp -std=c++11

[sly@VM-0-3-centos 20220114]$ ./a.out

1 3 5

=========================================================

// 使用第二组数据时,程序最终会崩溃

[sly@VM-0-3-centos 20220114]$ vim testVector.cpp

[sly@VM-0-3-centos 20220114]$ g++ testVector.cpp -std=c++11

[sly@VM-0-3-centos 20220114]$ ./a.out

Segmentation fault

从上述三个例子中可以看到:SGI STL中,迭代器失效后,代码并不一定会崩溃,但是运行

结果肯定不对,如果it不在begin和end范围内,肯定会崩溃的。

与vector类似,string在插入+扩容操作+erase之后,迭代器也会失效

#include <string>

void TestString()

{

string s("hello");

auto it = s.begin();

// 放开之后代码会崩溃,因为resize到20会string会进行扩容

// 扩容之后,it指向之前旧空间已经被释放了,该迭代器就失效了

// 后序打印时,再访问it指向的空间程序就会崩溃

//s.resize(20, '!');

while (it != s.end())

{

cout << *it;

++it;

}

cout << endl;

it = s.begin();

while (it != s.end())

{

it = s.erase(it);

// 按照下面方式写,运行时程序会崩溃,因为erase(it)之后

// it位置的迭代器就失效了

// s.erase(it);

++it;

}

}

迭代器失效解决办法:在使用前,对迭代器重新赋值即可。



声明

本文内容仅代表作者观点,或转载于其他网站,本站不以此文作为商业用途
如有涉及侵权,请联系本站进行删除
转载本站原创文章,请注明来源及作者。