vector 容器
vector 容器是 STL 中最常用的容器之一。vector 实现的是一个动态数组,即可以进行元素的插入和删除,在此过程中,vector 会动态调整所占用的内存空间,整个过程无需人工干预。
vector 常被称为向量容器,因为该容器擅长在尾部插入或删除元素,在常量时间内就可以完成,时间复杂度为O(1);而对于在容器头部或者中部插入或删除元素,则花费时间要长一些(移动元素需要耗费时间),时间复杂度为线性阶O(n)。
vector 容器以类模板 vector<T>( T 表示存储元素的类型)的形式定义在 <vector> 头文件中,并位于 std 命名空间中。
#include <vector>
using namespace std;
创建vector容器的几种方式
-
创建存储 double 类型元素的一个 vector 容器:
std::vector<double> values;这是一个空的 vector 容器,因为容器中没有元素,所以没有为其分配空间。当添加第一个元素(比如使用 push_back() 函数)时,vector 会自动分配内存。
-
在创建好空容器的基础上,通过调用 reserve() 成员函数来增加容器的容量:
values.reserve(20);这样就设置了容器的内存分配,即至少可以容纳 20 个元素。注意,如果 vector 的容量在执行此语句之前,已经大于或等于 20 个元素,那么这条语句什么也不做;另外,调用 reserve() 不会影响已存储的元素,也不会生成任何元素,即 values 容器内此时仍然没有任何元素。
如果调用 reserve() 来增加容器容量,之前创建好的任何迭代器(例如开始迭代器和结束迭代器)都可能会失效,这是因为,为了增加容器的容量,vector 容器的元素可能已经被复制或移到了新的内存地址。所以后续再使用这些迭代器时,最好重新生成一下。
-
创建的同时指定初始值以及元素个数:
std::vector<int> primes {2, 3, 5, 7, 11, 13, 17, 19};std::vector<double> values(20);values 容器开始时就有 20 个元素,它们的默认初始值都为 0。
如果不想用 0 作为默认值,也可以指定一个其它值:std::vector<double> values(20, 1.0);圆括号 () 中的 2 个参数,既可以是常量,也可以用变量来表示:
int num=20; double value =1.0; std::vector<double> values(num, value); -
通过存储元素类型相同的其它 vector 容器,也可以创建新的 vector 容器
std::vector<char>value1(5, 'c'); std::vector<char>value2(value1);value2 容器中也具有 5 个字符 ‘c’。在此基础上,如果不想复制其它容器中所有的元素,可以用一对指针或者迭代器来指定初始值的范围:
int array[]={1,2,3}; std::vector<int>values(array, array+2);//values 将保存{1,2} std::vector<int>value1{1,2,3,4,5}; std::vector<int>value2(std::begin(value1),std::begin(value1)+3);//value2保存{1,2,3}
vector容器的成员函数
| 函数成员 | 函数功能 |
|---|---|
| begin() | 返回指向容器中第一个元素的迭代器。 |
| end() | 返回指向容器最后一个元素所在位置后一个位置的迭代器,通常和 begin() 结合使用。 |
| rbegin() | 返回指向最后一个元素的迭代器。 |
| rend() | 返回指向第一个元素所在位置前一个位置的迭代器。 |
| cbegin() | 和 begin() 功能相同,只不过在其基础上,增加了 const 属性,不能用于修改元素。 |
| cend() | 和 end() 功能相同,只不过在其基础上,增加了 const 属性,不能用于修改元素。 |
| crbegin() | 和 rbegin() 功能相同,只不过在其基础上,增加了 const 属性,不能用于修改元素。 |
| crend() | 和 rend() 功能相同,只不过在其基础上,增加了 const 属性,不能用于修改元素。 |
| size() | 返回实际元素个数。 |
| max_size() | 返回元素个数的最大值。这通常是一个很大的值,一般是 2^32-1,所以我们很少会用到这个函数。 |
| resize() | 改变实际元素的个数。 |
| capacity() | 返回当前容量。 |
| empty() | 判断容器中是否有元素,若无元素,则返回 true;反之,返回 false。 |
| reserve() | 增加容器的容量。 |
| shrink _to_fit() | 将内存减少到等于当前元素实际所使用的大小。 |
| operator[ ] | 重载了 [ ] 运算符,可以向访问数组中元素那样,通过下标即可访问甚至修改 vector 容器中的元素。 |
| at() | 使用经过边界检查的索引访问元素。 |
| front() | 返回第一个元素的引用。 |
| back() | 返回最后一个元素的引用。 |
| data() | 返回指向容器中第一个元素的指针。 |
| assign() | 用新元素替换原有内容。 |
| push_back() | 在序列的尾部添加一个元素。 |
| pop_back() | 移出序列尾部的元素。 |
| insert() | 在指定的位置插入一个或多个元素。 |
| erase() | 移出一个元素或一段元素。 |
| clear() | 移出所有的元素,容器大小变为 0。 |
| swap() | 交换两个容器的所有元素。 |
| emplace() | 在指定的位置直接生成一个元素。 |
| emplace_back() | 在序列尾部生成一个元素。 |
#include <iostream>
#include <vector>
using namespace std;
int main()
{
//初始化一个空vector容量
vector<char>value;
//向value容器中的尾部依次添加 S、T、L 字符
value.push_back('S');
value.push_back('T');
value.push_back('L');
//调用 size() 成员函数容器中的元素个数
printf("元素个数为:%d\n", value.size());
//使用迭代器遍历容器
for (auto i = value.begin(); i < value.end(); i++) {
cout << *i << " ";
}
cout << endl;
//向容器开头插入字符
value.insert(value.begin(), 'C');
cout << "首个元素为:" << value.at(0) << endl;
return 0;
}
vector容器迭代器
vector 容器的迭代器也是随机访问迭代器,并且 vector 模板类提供的操作迭代器的成员函数也和 array 容器一样.
遍历访问容器中存储的元素
begin() 和 end() 成员函数
#include <iostream>
//需要引入 vector 头文件
#include <vector>
using namespace std;
int main()
{
vector<int>values{1,2,3,4,5};
auto first = values.begin();
auto end = values.end();
while (first != end)
{
cout << *first << " ";
++first;
}
return 0;
}
cbegin()/cend() 成员函数
#include <iostream>
//需要引入 vector 头文件
#include <vector>
using namespace std;
int main()
{
vector<int>values{1,2,3,4,5};
auto first = values.cbegin();
auto end = values.cend();
while (first != end)
{
//*first = 10;不能修改元素
cout << *first << " ";
++first;
}
return 0;
}
rbegin() 和 rend() 成员函数
#include <iostream>
//需要引入 vector 头文件
#include <vector>
using namespace std;
int main()
{
vector<int>values{1,2,3,4,5};
auto first = values.rbegin();
auto end = values.rend();
while (first != end)
{
cout << *first << " ";
++first;
}
return 0;
}
vector 容器可以随着存储元素的增加,自行申请更多的存储空间。因此,在创建 vector 对象时,我们可以直接创建一个空的 vector 容器,并不会影响后续使用该容器。在初始化空的 vector 容器时,不能使用迭代器。
#include <iostream>
#include <vector>
using namespace std;
int main()
{
vector<int>values;
int val = 1;
for (auto first = values.begin(); first < values.end(); ++first, val++) {
*first = val;
//初始化的同时输出值
cout << *first;
}
return 0;
}
vector 容器在申请更多内存的同时,容器中的所有元素可能会被复制或移动到新的内存地址,这会导致之前创建的迭代器失效。
#include <iostream>
#include <vector>
using namespace std;
int main()
{
vector<int>values{1,2,3};
cout << "values 容器首个元素的地址:" << values.data() << endl;
auto first = values.begin();
auto end = values.end();
//增加 values 的容量
values.reserve(20);
cout << "values 容器首个元素的地址:" << values.data() << endl;
while (first != end) {
cout << *first;
++first;
}
return 0;
}
为了保险起见,每当 vector 容器的容量发生变化时,我们都要对之前创建的迭代器重新初始化一遍:
#include <iostream>
#include <vector>
using namespace std;
int main()
{
vector<int>values{1,2,3};
cout << "values 容器首个元素的地址:" << values.data() << endl;
auto first = values.begin();
auto end = values.end();
//增加 values 的容量
values.reserve(20);
cout << "values 容器首个元素的地址:" << values.data() << endl;
first = values.begin();
end = values.end();
while (first != end) {
cout << *first ;
++first;
}
return 0;
}
vector容器访问元素的几种方式
访问vector容器中单个元素
-
vector 容器可以像普通数组那样访问存储的元素,甚至对指定下标处的元素进行修改:
#include <iostream> #include <vector> using namespace std; int main() { vector<int> values{1,2,3,4,5}; //获取容器中首个元素 cout << values[0] << endl; //修改容器中下标为 0 的元素的值 values[0] = values[1] + values[2] + values[3] + values[4]; cout << values[0] << endl; return 0; } -
vector 容器也提供了
at()成员函数,当传给 at() 的索引会造成越界时,会抛出std::out_of_range异常#include <iostream> #include <vector> using namespace std; int main() { vector<int> values{1,2,3,4,5}; //获取容器中首个元素 cout << values.at(0) << endl; //修改容器中下标为 0 的元素的值 values.at(0) = values.at(1) + values.at(2) + values.at(3) + values.at(4); cout << values.at(0) << endl; //下面这条语句会发生 out_of_range 异常 //cout << values.at(5) << endl; return 0; } -
vector 容器还提供了 2 个成员函数,即
front()和back(),它们分别返回 vector 容器中第一个和最后一个元素的引用,通过利用这 2 个函数返回的引用,可以访问(甚至修改)容器中的首尾元素。#include <iostream> #include <vector> using namespace std; int main() { vector<int> values{1,2,3,4,5}; cout << "values 首元素为:" << values.front() << endl; cout << "values 尾元素为:" << values.back() << endl; //修改首元素 values.front() = 10; cout <<"values 新的首元素为:" << values.front() << endl; //修改尾元素 values.back() = 20; cout << "values 新的尾元素为:" << values.back() << endl; return 0; } -
vector 容器还提供了
data()成员函数,该函数的功能是返回指向容器中首个元素的指针。通过该指针也可以访问甚至修改容器中的元素。#include <iostream> #include <vector> using namespace std; int main() { vector<int> values{1,2,3,4,5}; //输出容器中第 3 个元素的值 cout << *(values.data() + 2) << endl; //修改容器中第 2 个元素的值 *(values.data() + 1) = 10; cout << *(values.data() + 1) << endl; return 0; }
访问vector容器中多个元素
-
借助·
size()成员函数,该函数可以返回 vector 容器中实际存储的元素个数。#include <iostream> #include <vector> using namespace std; int main() { vector<int> values{1,2,3,4,5}; //从下标 0 一直遍历到 size()-1 处 for (int i = 0; i < values.size(); i++) { cout << values[i] << " "; } return 0; }这里不要使用 capacity() 成员函数,因为它返回的是 vector 容器的容量,而不是实际存储元素的个数,这两者是有差别的。
-
使用基于范围的循环
#include <iostream> #include <vector> using namespace std; int main() { vector<int> values{1,2,3,4,5}; for (auto&& value : values) cout << value << " "; return 0; } -
使用 vector 迭代器遍历 vector 容器
#include <iostream> #include <vector> using namespace std; int main() { vector<int> values{1,2,3,4,5}; for (auto first = values.begin(); first < values.end(); ++first) { cout << *first << " "; } return 0; }
vector添加元素(push_back()和emplace_back())
push_back()
该成员函数的功能是在 vector 容器尾部添加一个元素
#include <iostream>
#include <vector>
using namespace std;
int main()
{
vector<int> values{};
values.push_back(1);
values.push_back(2);
for (int i = 0; i < values.size(); i++) {
cout << values[i] << " ";
}
return 0;
}
emplace_back()
其功能和 push_back() 相同,都是在 vector 容器的尾部添加一个元素。
#include <iostream>
#include <vector>
using namespace std;
int main()
{
vector<int> values{};
values.emplace_back(1);
values.emplace_back(2);
for (int i = 0; i < values.size(); i++) {
cout << values[i] << " ";
}
return 0;
}
emplace_back()和push_back()的区别
emplace_back() 和 push_back() 的区别,就在于底层实现的机制不同。
- push_back() 向容器尾部添加元素时,首先会创建这个元素,然后再将这个元素拷贝或者移动到容器中(如果是拷贝的话,事后会自行销毁先前创建的这个元素);
- 而 emplace_back() 在实现时,则是直接在容器尾部创建这个元素,省去了拷贝或移动元素的过程。
#include <vector>
#include <iostream>
using namespace std;
class testDemo
{
public:
testDemo(int num):num(num){
std::cout << "调用构造函数" << endl;
}
testDemo(const testDemo& other) :num(other.num) {
std::cout << "调用拷贝构造函数" << endl;
}
testDemo(testDemo&& other) :num(other.num) {
std::cout << "调用移动构造函数" << endl;
}
private:
int num;
};
int main()
{
cout << "emplace_back:" << endl;
std::vector<testDemo> demo1;
demo1.emplace_back(2);
cout << "push_back:" << endl;
std::vector<testDemo> demo2;
demo2.push_back(2);
}
output
emplace_back:
调用构造函数
push_back:
调用构造函数
调用移动构造函数
将 testDemo 类中的移动构造函数注释掉,再运行程序会发现,运行结果变为:
emplace_back:
调用构造函数
push_back:
调用构造函数
调用拷贝构造函数
push_back() 在底层实现时,会优先选择调用移动构造函数,如果没有才会调用拷贝构造函数。
因此,在实际使用时,建议大家优先选用 emplace_back()。
vector插入元素(insert()和emplace())
insert()
nsert() 函数的功能是在 vector 容器的指定位置插入一个或多个元素。
#include <iostream>
#include <vector>
#include <array>
using namespace std;
int main()
{
std::vector<int> demo{1,2};
//第一种格式用法
demo.insert(demo.begin() + 1, 3);//{1,3,2}
//第二种格式用法
demo.insert(demo.end(), 2, 5);//{1,3,2,5,5}
//第三种格式用法
std::array<int,3>test{ 7,8,9 };
demo.insert(demo.end(), test.begin(), test.end());//{1,3,2,5,5,7,8,9}
//第四种格式用法
demo.insert(demo.end(), { 10,11 });//{1,3,2,5,5,7,8,9,10,11}
for (int i = 0; i < demo.size(); i++) {
cout << demo[i] << " ";
}
return 0;
}
emplace()
用于在 vector 容器指定位置之前插入一个新的元素。emplace() 每次只能插入一个元素,而不是多个。
#include <vector>
#include <iostream>
using namespace std;
int main()
{
std::vector<int> demo1{1,2};
//emplace() 每次只能插入一个 int 类型元素
demo1.emplace(demo1.begin(), 3);
for (int i = 0; i < demo1.size(); i++) {
cout << demo1[i] << " ";
}
return 0;
}
emplace() 在插入元素时,是在容器的指定位置直接构造元素,而不是先单独生成,再将其复制(或移动)到容器中。因此,在实际使用中,推荐大家优先使用 emplace()。
#include <vector>
#include <iostream>
using namespace std;
class testDemo
{
public:
testDemo(int num) :num(num) {
std::cout << "调用构造函数" << endl;
}
testDemo(const testDemo& other) :num(other.num) {
std::cout << "调用拷贝构造函数" << endl;
}
testDemo(testDemo&& other) :num(other.num) {
std::cout << "调用移动构造函数" << endl;
}
testDemo& operator=(const testDemo& other);
private:
int num;
};
testDemo& testDemo::operator=(const testDemo& other) {
this->num = other.num;
return *this;
}
int main()
{
cout << "insert:" << endl;
std::vector<testDemo> demo2{};
demo2.insert(demo2.begin(), testDemo(1));
cout << "emplace:" << endl;
std::vector<testDemo> demo1{};
demo1.emplace(demo1.begin(), 1);
return 0;
}
output
insert:
调用构造函数
调用移动构造函数
emplace:
调用构造函数
当拷贝构造函数和移动构造函数同时存在时,insert() 会优先调用移动构造函数。
vector删除元素的几种方式
pop_back()
#include <vector>
#include <iostream>
using namespace std;
int main()
{
vector<int>demo{ 1,2,3,4,5 };
demo.pop_back();
//输出 dmeo 容器新的size
cout << "size is :" << demo.size() << endl;
//输出 demo 容器新的容量
cout << "capacity is :" << demo.capacity() << endl;
for (int i = 0; i < demo.size(); i++) {
cout << demo[i] << " ";
}
return 0;
}
output
size is :4
capacity is :5
1 2 3 4
erase()
iterator erase (pos);
pos 为指定被删除元素位置的迭代器,同时该函数会返回一个指向删除元素所在位置下一个位置的迭代器。
#include <vector>
#include <iostream>
using namespace std;
int main()
{
vector<int>demo{ 1,2,3,4,5 };
auto iter = demo.erase(demo.begin() + 1);//删除元素 2
//输出 dmeo 容器新的size
cout << "size is :" << demo.size() << endl;
//输出 demo 容器新的容量
cout << "capacity is :" << demo.capacity() << endl;
for (int i = 0; i < demo.size(); i++) {
cout << demo[i] << " ";
}
//iter迭代器指向元素 3
cout << endl << *iter << endl;
return 0;
}
output
size is :4
capacity is :5
1 3 4 5
3
erase() 函数在删除元素时,会将删除位置后续的元素陆续前移,并将容器的大小减 1。
除了删除容器中单个元素,还可以删除容器中某个指定区域内的所有元素,同样可以使用 erase() 成员函数实现.
iterator erase (iterator first, iterator last);
其中 first 和 last 是指定被删除元素区域的迭代器,同时该函数会返回指向此区域之后一个位置的迭代器。
#include <vector>
#include <iostream>
using namespace std;
int main()
{
std::vector<int> demo{ 1,2,3,4,5 };
//删除 2、3
auto iter = demo.erase(demo.begin()+1, demo.end() - 2);
cout << "size is :" << demo.size() << endl;
cout << "capacity is :" << demo.capacity() << endl;
for (int i = 0; i < demo.size(); i++) {
cout << demo[i] << " ";
}
return 0;
}
output
size is :3
capacity is :5
1 4 5
结合 swap() 和 pop_back() 函数
同样可以实现删除容器中指定位置元素的目的。
swap() 函数在头文件 <algorithm> 和 <utility>中都有定义,使用时引入其中一个即可。
#include <vector>
#include <iostream>
#include <algorithm>
using namespace std;
int main()
{
vector<int>demo{ 1,2,3,4,5 };
//交换要删除元素和最后一个元素的位置
swap(*(std::begin(demo)+1),*(std::end(demo)-1));//等同于 swap(demo[1],demo[4])
//交换位置后的demo容器
for (int i = 0; i < demo.size(); i++) {
cout << demo[i] << " ";
}
demo.pop_back();
cout << endl << "size is :" << demo.size() << endl;
cout << "capacity is :" << demo.capacity() << endl;
//输出demo 容器中剩余的元素
for (int i = 0; i < demo.size(); i++) {
cout << demo[i] << " ";
}
return 0;
}
output
1 5 3 4 2
size is :4
capacity is :5
1 5 3 4
remove()
删除容器中和指定元素值相同的所有元素,该函数定义在 <algorithm> 头文件中.
#include <vector>
#include <iostream>
#include <algorithm>
using namespace std;
int main()
{
vector<int>demo{ 1,3,3,4,3,5 };
//删除元素3
auto iter = std::remove(demo.begin(), demo.end(), 3);
cout << "size is :" << demo.size() << endl;
cout << "capacity is :" << demo.capacity() << endl;
//输出剩余的元素
for (auto first = demo.begin(); first < iter;++first) {
cout << *first << " ";
}
return 0;
}
output
size is :6
capacity is :6
1 4 5
在对容器执行完 remove() 函数之后,由于该函数并没有改变容器原来的大小和容量,因此无法使用之前的方法遍历容器,而是需要向程序中那样,借助 remove() 返回的迭代器完成正确的遍历。
remove() 的实现原理是,在遍历容器中的元素时,一旦遇到目标元素,就做上标记,然后继续遍历,直到找到一个非目标元素,即用此元素将最先做标记的位置覆盖掉。因此,如果将上面程序中 demo 容器的元素全部输出,得到的结果为1 4 5 4 3 5
既然通过 remove() 函数删除掉 demo 容器中的多个指定元素,该容器的大小和容量都没有改变,其剩余位置还保留了之前存储的元素。我们可以使用 erase() 成员函数删掉这些 “无用” 的元素。
#include <vector>
#include <iostream>
#include <algorithm>
using namespace std;
int main()
{
vector<int>demo{ 1,3,3,4,3,5 };
//删除元素3
auto iter = std::remove(demo.begin(), demo.end(), 3);
demo.erase(iter, demo.end());
cout << "size is :" << demo.size() << endl;
cout << "capacity is :" << demo.capacity() << endl;
//输出剩余的元素
for (int i = 0; i < demo.size();i++) {
cout << demo[i] << " ";
}
return 0;
}
output
size is :3
capacity is :6
1 4 5
clear()
#include <vector>
#include <iostream>
#include <algorithm>
using namespace std;
int main()
{
vector<int>demo{ 1,3,3,4,3,5 };
//删除所有元素
demo.clear();
cout << "size is :" << demo.size() << endl;
cout << "capacity is :" << demo.capacity() << endl;
return 0;
}
output
size is :0
capacity is :6
本文详细介绍了C++标准库中的STL向量容器(vector),包括其动态数组特性、创建与初始化方式、内存管理如reserve()和shrink_to_fit()、成员函数如push_back()、insert()和erase(),以及迭代器的使用和失效情况。此外,还探讨了emplace_back()和push_back()的区别以及如何高效地添加和删除元素。
1370

被折叠的 条评论
为什么被折叠?



