C++ :vector的介绍和使用
vector学习时一定要学会查看reference
目录
前言
一、vector基本概念
1.1vector是什么?
1.2内存管理
二、vector的使用
2.1vector的构造
2.2vector iterator 的使用
2.3vector 空间增长问题
2.4vector的元素访问
2.5vector 增删查改
总结
前言
在C++编程中,vector 是标准模板库(STL)中最常用的数据结构之一。它是一个封装了动态大小数组的序列容器,提供了灵活的内存管理方式和高效的元素访问机制。小编将介绍 vector 的基本特性、常用操作,帮助你更好地理解和掌握这一强大的工具。
一、vector基本概念
1.1vector是什么?
vector是表示可变大小数组的序列容器。
- vector就像数组一样,vector也采用的连续存储空间来存储元素。也就是意味着可以采用下标对vector的元素进行访问,和数组一样高效。但是又不像数组,它的大小是可以动态改变的,而且它的大小会被容器自动处理
- 与普通的数组相比, vector 的大小可以动态变化,能够根据需要自动扩展或收缩。它通过模板实现,可以存储任意类型的元素,如整数、浮点数、字符串等。
1.2内存管理
vector 在内部使用动态分配的内存来存储元素。当向 vector 中添加元素时,如果当前分配的内存不足以容纳新元素, vector 会自动分配一块更大的内存,并将原有元素复制到新的内存中。这个过程称为“扩容”。扩容操作虽然在某些情况下会带来性能开销,但 vector 通过合理的内存分配策略(如每次扩容时将容量加倍)尽量减少了扩容的频率。
二、vector的使用
2.1vector的构造
空容器构造函数 (默认构造函数)
构造一个空容器,没有元素。
#include <iostream>
#include <vector>
using namespace std;
int main ()
{// constructors used in the same order as described above:vector<int> first; // 无参构造return 0;
}
填写构造函数
构造一个容器n元素。每个元素都是val(如果提供)。
int main ()
{std::vector<int> second (4,100); // 构造并初始化n个valreturn 0;
}
范围构造函数
构造一个包含与范围一样多的元素的容器[begin(),end()),与每个元素emplace-构造从该范围内的相应元素,以相同的顺序。
int main ()
{vector<int> second (4,100); // vector<int> third (second.begin(),second.end()); // 使用迭代器进行初始化构造return 0;
}
(迭代器目前可以认为是指针)
拷贝构造函数
int main ()
{vector<int> second (4,100); // four ints with value 100vector<int> third (second.begin(),second.end()); // iterating through secondvector<int> fourth (third); // 拷贝构造return 0;
}
2.2vector iterator 的使用
获取第一个数据位置的iterator/const_iterator:
获取最后一个数据的下一个位置的iterator/const_iterator
获取最后一个数据位置的reverse_iterator
获取第一个数据前一个位置的reverse_iterator
2.3vector 空间增长问题
获取数据个数:
获取容量大小:
capacity的代码在vs和g++下分别运行会发现,vs下capacity是按1.5倍增长的,g++是按2倍增长的。
这个问题经常会考察,不要固化的认为,vector增容都是2倍,具体增长多少是根据具体的需求定义的。vs是PJ版本STL,g++是SGI版本STL。
判断是否为空:
改变vector的size:
- resize 指定的数据个数如果大于当前一段空间上的有效数据个数那么如果不指定数据会使用 0 作为有效数据追加在原数据序列后面,让有效字符个数变为指定字符个数,并且空间不够会扩容开空间,如果我们指定了数据则会使用我们指定的数据作为有效数据追加在原数据序列的后面
- resize指定的数据个数如果小于当前有效数据个数,那么会将有效数据个数修改为指定数据个数,并且底层空间大小保持不变
改变vector的capacity:
reserve只负责开辟空间,如果确定知道需要用多少空间,reserve可以缓解vector增容的代价缺陷问题。
- reserve 会一次性扩容开空间,如果你预想开的空间大于当前空间大小,那么开好的空间相较于你预期想要开的空间只多不少,reverse一般用于你提前已经预想好开多大空间了,这样使用reserve进行提前一次性开好空间提高效率,避免多次开空间
- 如果你预想开的空间小于等于当前空间大小,那么当前空间大小保持不变
int main()
{
vector<int> v;
v.push_back(1);
v.push_back(1);
v.push_back(1);
v.push_back(1);
cout<<v.size();
cout<<v.capacity();
v.resize(10);
cout<<v.size();
v.reserve(10);
cout<<capacity();
return 0;
}
2.4vector的元素访问
operator[] :用于提供对容器或数组中元素的快速访问
与 at() 的区别:
operator[] :
• 不进行边界检查。如果索引超出范围,行为是未定义的(可能导致程序崩溃)。
• 返回的是元素的引用,可以直接修改元素的值。
• 性能较高,因为它不进行额外的检查。
at() :
• 进行边界检查。如果索引超出范围,会抛出 std::out_of_range 异常。
• 返回的是元素的引用,也可以直接修改元素的值。
• 性能略低于 operator[] ,因为需要进行额外的检查。
front:返回对第一个元素的引用向量。
back:返回对最后一个元素的引用向量。
int main()
{int a[5] = { 1,2,3,4,5 };vector<int> v(a, a + 5);cout << v.at(1) << endl;cout << v.front() << endl;cout << v.back() << endl;return 0;
}
2.5vector 增删查改
push_back:尾插
int main()
{
vector<int> v;
v.push_back(1);
v.push_back(1);
v.push_back(1);
v.push_back(1);
return 0;
}
pop_back:尾删
int main()
{
vector<int> v;
v.push_back(1);
v.push_back(1);
v.push_back(1);
v.push_back(1);
v.pop_back();return 0;
}
insert:在pos位置插入一个值/一个区间/n个值
int main()
{int a[5] = { 1,2,3,4,5 };vector<int> v(a, a + 5);// v=1,2,3,4,5v.insert(v.begin(), 5);//5,1,2,3,4,5for (int e : v){cout << e << ' ';}cout << endl;v.insert(v.begin(), 5 , 1);//1,1,1,1,1,5,1,2,3,4,5for (int e : v){cout << e << ' ';}cout << endl;vector<int> v0(3, 6);v.insert(v.end(), v0.begin(),v0.end());//1,1,1,1,1,5,1,2,3,4,5,6,6,6for (auto e : v){cout << e << ' ';}cout << endl;return 0;
}
assign:将要传入数据的对象的原有的数据清空,再去分派数据
int main()
{string s("hello world");vector<char> v;v.assign(s.begin(), s.end());for (char e : v){cout << e;//"hello world"}cout << endl;v.assign(6,6);for (char e : v){cout << e << ' ';//6,6,6,6,6,6}cout << endl;return 0;
}
erase:删除一个迭代器位置上的值并且返回这个删除的迭代器的下一个数据的位置的迭代器
注意:由于当前迭代器位置的值已经被删除,所以当前位置的迭代器位置的值的下一个位置的值会向前顶替当前位置的值,那么下一个数据的值就成了当前位置上的数据,返回的是当前位置的迭代器,但当前位置的迭代器上的值改变了
swap:交换两个vector的数据空间
int main()
{vector<int> v1(6, 1);vector<int> v2(6, 0);cout << "v1的数据序列为:";//1,1,1,1,1,1for (auto e : v1){cout << e << ' ';}cout << endl;cout << "v2的数据序列为:";//0,0,0,0,0,0for (auto e : v2){cout << e << ' ';}cout << endl << endl;v1.swap(v2);//v1和v2交换了数据空间cout << "v1的数据序列为:";for (auto e : v1){cout << e << ' ';//0,0,0,0,0,0}cout << endl;cout << "v2的数据序列为:";for (auto e : v2){cout << e << ' ';//1,1,1,1,1,1}cout << endl;return 0;
}
clear:清空vector类模板实例化出的对象的全部数据
int main()
{vector<int> v1(6, 1);v1.clear();//v1被清空return 0;
}
总结
vector的使用场景:
- 动态数组需求当需要一个大小可动态变化的数组时, vector 是理想的选择。它能够根据元素的添加和删除自动调整内存分配,避免了手动管理内存的复杂性。
- 高效的随机访问 vector 支持高效的随机访问,通过索引可以快速获取任意位置的元素。这使得它在需要频繁随机访问元素的场景中非常高效。
- 与其他STL算法配合 vector 作为STL容器,能够无缝地与STL算法(如排序、查找等)配合使用,从而实现复杂的数据处理逻辑。
Vector的注意事项:
- 虽然 vector 的扩容策略尽量减少了扩容的频率,但在某些高频插入的场景中,扩容仍然可能带来一定的性能开销。如果事先知道 vector 的大致大小,可以通过 reserve() 方法预先分配足够的内存,从而避免多次扩容。
- 迭代器失效在对 vector 进行插入或删除操作时,可能会导致迭代器失效。特别是当插入或删除操作导致内存扩容或收缩时,所有迭代器都会失效。因此,在使用迭代器时需要注意迭代器的有效性。