文章目录
序列容器分类
- array<T, N>[数组容器]
一个固定长度的序列,有N个T类型的对象,不能增加或删除元素。
- vector[向量容器]
一个可变长度的序列,用来存放T类型对象,可自动增加容量,只能在末尾高效地增加或删除元素。
- deque[双向队列容器]
一个可变长度的序列,用来存放T类型对象,可自动增加容量,能在两端增减或删除元素,但效率不高。
- list[链表容器]
一个可变长度的序列,用来存放T类型对象,可自动增加容量,能在任意位置高效增减或删除元素,只能从链表起始/结尾元素开始查找元素,因此在访问任意位置元素时效率不高。
array数组容器
初始化
- 初始化一个可容纳100个double元素的array变量。
#include <array>
int main(int argc, char * argv[]) {
std::array<double, 100> datas;
return 0;
}
- 初始化一个可容纳100个double元素的array变量,并初始化为0.0。
#include <array>
int main(int argc, char * argv[]) {
std::array<double, 100> datas {};
return 0;
}
- 初始化一个可容纳100个double元素的array变量,并将前5个元素初始化为0.5、1.0、1.5、2.0、2.5,其余初始化为0.0。
#include <array>
int main(int argc, char * argv[]) {
std::array<double, 100> datas {0.5, 1.0, 1.5, 2.0, 2.5};
return 0;
}
- 初始化一个可容纳100个double元素的array变量,并将元素初始化为0.5。
#include <array>
int main(int argc, char * argv[]) {
std::array<double, 100> datas;
datas.fill(0.5);
return 0;
}
访问元素
- 通过
[index]
的方式访问元素。
#include <iostream>
#include <array>
int main(int argc, char * argv[]) {
std::array<double, 5> datas {0.5, 1.0, 1.5, 2.0, 2.5};
double result = datas[0] + 2.0 * datas[3];
std::cout << "result = " << result << std::endl;
return 0;
}
// 输出结果
// result = 4.5
- 通过
at(index)
的方式访问元素。
#include <iostream>
#include <array>
int main(int argc, char * argv[]) {
std::array<double, 5> datas {0.5, 1.0, 1.5, 2.0, 2.5};
double result = datas.at(0) + 2.0 * datas.at(3);
std::cout << "result = " << result << std::endl;
return 0;
}
// 输出结果
// result = 4.5
[index]
和at(index)
两种方式都能访问到指定位置的元素,但是索引位置越界时,前者会进行越界检查
。而后者会抛出std::out_of_rang
异常错误使程序崩溃。由于[index]进行了越界检查,因此性能会比at(index)低一些
。
- 通过
size()
获取容器中的元素数量。
#include <iostream>
#include <array>
int main(int argc, char * argv[]) {
std::array<double, 5> datas {0.5, 1.0, 1.5, 2.0, 2.5};
double sum = 0.0;
for (int index = 0; index < datas.size(); ++index) {
sum += datas.at(index);
}
std::cout << "sum = " << sum << std::endl;
return 0;
}
// 输出结果
// sum = 7.5
对于array容器来说size()函数最大的用处可能就是用于条件循环遍历,因为array容器在初始化时明确声明容器大小的,切不可删除或者增加。
迭代器
- 迭代器对象可以使用
begin()
和end()
返回,这两个函数前者返回容器的起始位置,后者返回容器的结束位置。
#include <iostream>
#include <array>
int main(int argc, char * argv[]) {
std::array<double, 5> datas {0.5, 1.0, 1.5, 2.0, 2.5};
double result = 0.0;
std::array<double, 5>::iterator first = datas.begin();
while (first != datas.end()) {
result += *first++;
}
std::cout << "result = " << result << std::endl;
return 0;
}
// 输出结果
// result = 7.5
- 通过
模板iota()函数
和迭代器可以完成对容器的递增初始化。
#include <iostream>
#include <array>
#include <numeric>
int main(int argc, char * argv[]) {
std::array<double, 5> datas {};
std::iota(datas.begin(), datas.end(), 1);
int index = 0;
std::array<double, 5>::iterator first = datas.begin();
while (first != datas.end()) {
std::cout << "[" << index++ << "] = " << *first++ << std::endl;
}
return 0;
}
// 输出结果
// [0] = 1
// [1] = 2
// [2] = 3
// [3] = 4
// [4] = 5
- 通过
rbegin()
和rend()
可以将容器反向遍历。
#include <iostream>
#include <array>
#include <numeric>
int main(int argc, char * argv[]) {
std::array<double, 5> datas {};
std::iota(datas.begin(), datas.end(), 1);
int index = 0;
std::array<double, 5>::reverse_iterator first = datas.rbegin();
while (first != datas.rend()) {
std::cout << "[" << index++ << "] = " << *first++ << std::endl;
}
return 0;
}
// 输出结果
// [0] = 5
// [1] = 4
// [2] = 3
// [3] = 2
// [4] = 1
容器比较
- 使用
比较
运算符可以对两个array容器进行比较。
#include <iostream>
#include <array>
#include <numeric>
int main(int argc, char * argv[]) {
std::array<double, 5> datas_5_f {};
std::iota(datas_5_f.begin(), datas_5_f.end(), 1);
std::array<double, 5> datas_5_s {};
std::iota(datas_5_s.begin(), datas_5_s.end(), 1);
std::array<double, 5> datas_5_e {};
std::iota(datas_5_e.rbegin(), datas_5_e.rend(), 1);
std::array<double, 6> datas_5_d {};
std::iota(datas_5_d.begin(), datas_5_d.end(), 1);
std::cout << "datas_5_f == datas_5_s ? " << (datas_5_f == datas_5_s ? "是" : "否") << std::endl;
std::cout << "datas_5_f == datas_5_e ? " << (datas_5_f == datas_5_e ? "是" : "否") << std::endl;
// std::cout << "datas_5_f == datas_5_d ? " << (datas_5_f == datas_5_d ? "是" : "否") << std::endl; 不能比较
return 0;
}
注意大小不一致的数组容器不能比较。
vector向量容器
初始化
- 初始化一个空的vector容器,并通过
reserve()
函数可以向一个空的容器分配指定个数的容量。
#include <vector>
int main(int argc, char * argv[]) {
std::vector<double> values;
values.reserve(20);
return 0;
}
如果当前容中的元素个数大于20元素,则reserve()无效。
- 使用
初始化列表
来指定初始值以及元素个数。
#include <vector>
int main(int argc, char * argv[]) {
std::vector<double> values {0.5, 0.7, 0.9, 1.1};
return 0;
}
- 使用构造方法初始化容器大小。
#include <vector>
int main(int argc, char * argv[]) {
std::vector<double> values(20);
return 0;
}
使用这种方法时可以将其中元素都初始化为0。注意在变量后边使用{}
和()
是不一样的,如下所示:
#include <vector>
int main(int argc, char * argv[]) {
std::vector<int> values {10}; // 初始化一个具体元素,容量是1
std::vector<int> values (10); // 初始化10个默认元素,容量是10
return 0;
}
- 使用构造方法初始化容器大小,并指定默认值。
#include <vector>
int main(int argc, char * argv[]) {
std::vector<double> values(20, 0.5);
return 0;
}
使用构造方法初始化的时候,指定的容量大小和默认初始值不用必须是常量,可以经过表达式计算的结果。
- 使用
std::begin
和std::end
用一个元素类型相同
,但容器类型不必相同
进行初始化。
#include <vector>
#include <array>
int main(int argc, char * argv[]) {
std::array<double, 5> datas {0.5, 1.0, 1.5, 2.0, 2.5};
std::vector<double> values {std::begin(datas), std::end(datas)};
return 0;
}
容量和扩容
vector的容量是指在不分配更多内存的情况下可以保存的最多元素。
- 通过
size()
可获取容器中当前存在的实际元素数量,通过capacity()
可以获取容器可以存放的最大元素数量。
#include <vector>
#include <iostream>
int main(int argc, char * argv[]) {
std::vector<double> datas {0.5, 1.0, 1.5, 2.0, 2.5};
datas.reserve(6);
std::cout << "vector size = " << datas.size() << std::endl;
std::cout << "vector capacity = " << datas.capacity() << std::endl;
return 0;
}
// 输出结果
// vector size = 5
// vector capacity = 6
- 动态扩容
当元素实际数量超过当前容器最大存储范围后,容器自动增加最大容量常见的一般说是2倍,但每次增加的容量取决于当前STL算法的实现
。
访问元素
- 通过
[index]
和at[index]
获取数据,这两个方法的使用与数组容器一致
,不同的是[index]
也存在容器越界异常。
#include <vector>
#include <iostream>
int main(int argc, char * argv[]) {
std::vector<double> datas {0.5, 1.0, 1.5, 2.0, 2.5};
std::cout << datas[0] << std::endl;
std::cout << datas.at(0) << std::endl;
// std::cout << datas[6] << std::endl; 抛出异常
return 0;
}
- 通过
front()
和back()
分别返回第一个元素
和最后一个元素
的引用
。
#include <vector>
#include <iostream>
int main(int argc, char * argv[]) {
std::vector<double> datas {0.5, 1.0, 1.5, 2.0, 2.5};
std::cout << datas.front() << std::endl;
std::cout << datas.back() << std::endl;
return 0;
}
- 通过
data()
获取执行容器元素存储内存的首地址。
#include <vector>
#include <iostream>
int main(int argc, char * argv[]) {
std::vector<double> datas {0.5, 1.0, 1.5, 2.0, 2.5};
std::cout << datas.data() << std::endl;
std::cout << datas.front() << std::endl;
return 0;
}
// 输出结果
// 000002A07695BEA0
// 0.5
迭代器
- 通过
back_inserter()
获取一个向后插入的迭代器。
#include <vector>
#include <iostream>
int main(int argc, char * argv[]) {
std::vector<double> datas {0.5, 1.0, 1.5, 2.0, 2.5};
auto vector_it = datas.begin();
while (vector_it != datas.end()) {
std::cout << *vector_it++ << std::endl;
}
return 0;
}
添加元素
- 通过
push_back()
向容器的尾端添加新元素。
#include <vector>
#include <iostream>
int main(int argc, char * argv[]) {
std::vector<double> datas {0.5, 1.0, 1.5, 2.0, 2.5};
datas.push_back(3.0);
auto vector_it = datas.begin();
while (vector_it != datas.end()) {
std::cout << *vector_it++ << std::endl;
}
return 0;
}
- 通过
emplace_back()
添加元素,该函的参数是可变长参数,可变参数的内容与要插入的元素的构造相匹配。
#include <vector>
#include <string>
#include <iostream>
class Person {
public:
Person(int _age, std::string _name) {
std::cout << "年龄 = " << _age << std::endl;
std::cout << "姓名 = " << _name << std::endl;
}
};
int main(int argc, char * argv[]) {
std::vector<Person> infos;
infos.emplace_back(28, "SJQ");
return 0;
}
push_back()函数底层调用的也是emplace_back()函数,但直接调用push_back()函数会进行move()操作。因此,使用emplace_back()效率会高一些。
- 通过
emplace_back()
函数在指定位置插入元素,该函数第一个参数是一个迭代器,该迭代器指向的位置就是插入元素的位置。
#include <utility>
#include <vector>
#include <string>
#include <iostream>
class Person {
public:
Person(int _age, std::string _name) {
age_ = _age;
name_ = std::move(_name);
}
int age_;
std::string name_;
};
int main(int argc, char * argv[]) {
std::vector<Person> infos;
infos.emplace_back(20, "S");
infos.emplace_back(22, "Q");
infos.emplace(++infos.begin(), 21, "J"); // 在第一个元素后边插入元素
auto vector_it = infos.begin();
while (vector_it != infos.end()) {
std::cout << (*vector_it++).name_;
}
return 0;
}
// 输出结果
// SJQ
- 通过
insert()
函数插入一个或者多个元素,该函数的第一个参数是插入的迭代器位置,后边的参数是具体元素或者是一个范围迭代器。
#include <vector>
#include <string>
#include <iostream>
int main(int argc, char * argv[]) {
std::vector<int> infos {2, 10};
std::vector<int> temps {4, 6, 8};
infos.insert(--infos.end(), temps.begin(), temps.end());
auto vector_it = infos.begin();
while (vector_it != infos.end()) {
std::cout << (*vector_it++) << " ";
}
return 0;
}
// 输出结果
// 2 4 6 8 10
删除元素
- 通过
clear()
函数可以将容器中的全部元素清空,但不影响容器的容量。
#include <vector>
#include <string>
#include <iostream>
int main(int argc, char * argv[]) {
std::vector<int> infos {2, 4, 6, 8, 10};
infos.clear();
std::cout << "vector size = " << infos.size() << std::endl;
std::cout << "vector capacity = " << infos.capacity() << std::endl;
return 0;
}
- 通过
pop_back()
函数删除容器结尾的元素。
#include <vector>
#include <string>
#include <iostream>
int main(int argc, char * argv[]) {
std::vector<int> infos {2, 4, 6, 8, 10};
infos.pop_back();
std::cout << "vector size = " << infos.size() << std::endl;
std::cout << "vector capacity = " << infos.capacity() << std::endl;
return 0;
}
- 通过
shrink_to_fit()
函数删除多余的容量
。
#include <vector>
#include <string>
#include <iostream>
int main(int argc, char * argv[]) {
std::vector<int> infos {2, 4, 6, 8, 10};
infos.reserve(10);
std::cout << "vector size = " << infos.size() << std::endl;
std::cout << "vector capacity = " << infos.capacity() << std::endl;
infos.shrink_to_fit();
std::cout << "vector size = " << infos.size() << std::endl;
std::cout << "vector capacity = " << infos.capacity() << std::endl;
return 0;
}
- 通过
erase()
函数删除一个元素。
#include <vector>
#include <string>
#include <iostream>
int main(int argc, char * argv[]) {
std::vector<int> infos {2, 4, 6, 8, 10, 12, 14, 16, 18};
auto iter_1 = infos.erase(std::begin(infos) + 1);
auto vector_it = infos.begin();
while (vector_it != infos.end()) {
std::cout << (*vector_it++) << " ";
}
return 0;
}
// 输出结果
// 2 6 8 10 12 14 16 18
- 通过
erase()
函数删除一个范围内的元素。
#include <vector>
#include <string>
#include <iostream>
int main(int argc, char * argv[]) {
std::vector<int> infos {2, 4, 6, 8, 10, 12, 14, 16, 18};
auto iter = infos.erase(std::begin(infos) + 1, std::end(infos) - 1);
auto vector_it = infos.begin();
while (vector_it != infos.end()) {
std::cout << (*vector_it++) << " ";
}
return 0;
}
// 输出结果
// 2 18
- 删除后返回的迭代器是删除元素所在位置的下一个元素位置。
#include <vector>
#include <string>
#include <iostream>
int main(int argc, char * argv[]) {
std::vector<int> infos {2, 4, 6, 8, 10, 12, 14, 16, 18};
auto iter = infos.erase(std::begin(infos) + 5);
while (iter != infos.end()) {
std::cout << (*iter++) << " ";
}
return 0;
}
// 输出结果
// 14 16 18
deque双向队列容器
初始化
deque容器的初始化方法与vector容器的初始化方法基本一致,只是没有vector的容量初始化。
#include <deque>
int main(int argc, char * argv[]) {
std::deque<double> values_1 {0.5, 0.7, 0.9, 1.1};
std::deque<double> values_2(20);
std::deque<double> values_3(20, 0.5);
std::array<double, 5> arrays {0.5, 1.0, 1.5, 2.0, 2.5};
std::deque<double> values_4 {std::begin(datas), std::end(datas)};
return 0;
}
访问元素
- 通过
[index]
和at[index]
获取数据,这两个方法的使用与向量容器一致
。
#include <deque>
#include <iostream>
int main(int argc, char * argv[]) {
std::deque<double> values {0.5, 1.0, 1.5, 2.0, 2.5};
std::cout << values[0] << std::endl;
std::cout << values.at(0) << std::endl;
// std::cout << values[6] << std::endl; 抛出异常
return 0;
}
- 通过
front()
和back()
分别返回第一个元素
和最后一个元素
的引用
。
#include <deque>
#include <iostream>
int main(int argc, char * argv[]) {
std::deque<double> values {0.5, 1.0, 1.5, 2.0, 2.5};
std::cout << values.front() << std::endl;
std::cout << values.back() << std::endl;
return 0;
}
添加和删除元素
push_front()
和pop_front()
可以在容器的头部插入元素,deque容器同样有push_back()
和pop_back()
,使用方法与vector一致。
#include <deque>
#include <iostream>
int main(int argc, char * argv[]) {
std::deque<double> values;
values.push_front(1.2);
values.push_back(2.2);
values.push_front(1.4);
values.push_back(2.4);
auto cur_it = values.begin();
while (cur_it != values.end()) {
std::cout << (*cur_it++) << " ";
}
return 0;
}
// 输出结果
// 1.4 1.2 2.2 2.4
#include <deque>
#include <iostream>
int main(int argc, char * argv[]) {
std::deque<double> values;
// ......
values.pop_front();
values.pop_back();
auto cur_it = values.begin();
while (cur_it != values.end()) {
std::cout << (*cur_it++) << " ";
}
return 0;
}
// 输出结果
// 1.2 2.2
- 通过
emplace_back()
和emplace_back()
函数,前者和vector容器一样在指定位置之后插入一个元素,后者则是在指定位置之前插入一个元素。
#include <deque>
#include <iostream>
int main(int argc, char * argv[]) {
std::deque<double> values {1.0, 1.5, 2.0, 2.5};
values.emplace_front(1.3);
values.emplace_back(1.5);
auto cur_it = values.begin();
while (cur_it != values.end()) {
std::cout << (*cur_it++) << " ";
}
return 0;
}
list链表容器
初始化
list容器的初始化方法与vector容器和deque容器的初始化方法基本一致。
#include <list>
int main(int argc, char * argv[]) {
std::list<double> values_1 {0.5, 0.7, 0.9, 1.1};
std::list<double> values_2(20);
std::list<double> values_3(20, 0.5);
std::array<double, 5> arrays {0.5, 1.0, 1.5, 2.0, 2.5};
std::list<double> values_4 {std::begin(datas), std::end(datas)};
return 0;
}
访问元素
list容器既没有at(index)
也没有[index]
,可以通过迭代器遍历访问。
- 通过
begin()
和end()
函数进行正向迭代。
#include <list>
#include <iostream>
int main(int argc, char * argv[]) {
std::list<double> values {1.0, 1.5, 2.0, 2.5};
auto cur_it = values.begin();
while (cur_it != values.end()) {
std::cout << (*cur_it++) << " ";
}
return 0;
}
- 通过
rbegin()
和rend()
函数进行逆序迭代。
#include <list>
#include <iostream>
int main(int argc, char * argv[]) {
std::list<double> values {1.0, 1.5, 2.0, 2.5};
auto cur_it = values.rbegin();
while (cur_it != values.rend()) {
std::cout << (*cur_it++) << " ";
}
return 0;
}