C++ vector容器的用法(非常详细)
C++ 标准库提供了各种类型的容器来存储对象集合,包括序列容器(如 vector、array 和 list)、有序和无序关联容器(如 set 和 map),以及不存储数据但提供面向序列容器的适配接口(如 stack 和 queue)的容器适配器。
标准库中的容器都是作为类模板实现的,这意味着它们可以与任意类型一起使用(只要它满足容器需求)。通常,应该始终使用最适合于特定问题的容器,这不仅在插入、删除、访问元素和内存使用速度方面提供了良好的性能,而且还使代码易于阅读和维护。
本节讲解 vector 容器的用法。
要初始化 std::vector 类模板,可以使用以下方法中的任何一种(但不限于这些):
1) 从初始化列表进行初始化:
2) 从数组初始化:
3) 从另一个容器初始化:
4) 从计数和值初始化:
要修改 std::vector 的内容,可以使用以下方法中的任何一种(但不限于这些):
1) 使用 push_back() 在 vector 的末尾添加一个元素:
2) 使用 pop_back() 从 vector 的末尾移除一个元素:
3) 使用 insert() 函数在 vector 的任意位置插入元素:
4) 通过使用 emplace_back() 方法在 vector 的末尾创建一个元素来添加一个元素:
5) 通过使用 emplace() 方法在 vector 的任意位置创建一个元素来插入一个元素:
要修改 vector 的整个内容,可以使用以下方法中的任何一种(但不限于这些):
1) 用另外一个 vector 进行赋值(使用 operator=),这将替换容器的内容:
2) 通过 assign() 方法用 begin 和 end 迭代器定义的另一个序列进行赋值,这将替换容器的内容:
3) 使用 swap() 方法交换两个 vector 的内容:
4) 使用 clear() 方法删除所有元素:
5) 使用 erase() 方法删除一个或多个元素(这需要一个迭代器或一对迭代器来定义 vector 中要删除的元素的范围):
要获取 vector 中第一个元素的地址,通常需要将 vector 的内容传递给类 C API,可以使用以下方法中的任意一种:
1) 使用 data() 方法,它返回指向第一个元素的指针,提供对存储 vector 元素的底层连续内存块的直接访问,这只在 C++11 之后可用:
2) 获取第一个元素的地址:
3) 获取 front() 方法引用的元素的地址:
4) 获取由 begin() 返回的迭代器所指向的元素的地址:
使用 vector 代替 array 有很多好处,包括:
注意,vector 用于存储对象实例。如果需要存储指针,请不要存储原始指针,而要存储智能指针。否则,需要管理指向对象的生命周期。
vector 类是一个非常高效的容器,它的所有实现都提供了许多优化,而大多数开发人员无法对 array 进行这些优化。对其元素的随机访问以及在 vector 末尾的插入和删除是常数 0(1) 操作(前提是不需要重新分配内存),而在其他任何地方的插入和删除是线性 0(n) 操作。
与其他标准容器相比,vector 具有很多优点:
std::vector 在语义上与数组非常相似,但其大小是可变的,可以增大,也可以减小。以下两个属性定义了 vector 的大小:
大小总是小于或等于容量,当大小等于容量时,并且需要添加一个新元素时,就需要修改容量,以便 vector 有容纳更多元素的空间。在这种情况下,vector 会分配一个新的内存块,并在释放已分配的内存之前将已有的内容移动到新的内存块。虽然这听起来很耗费时间,事实上也确实如此,但在每次需要更改时,vector 的实现都会成倍数地增加容量。平均而言,vector 的每个元素只需要移动一次(这是因为在增加容量的过程中,vector 的所有元素都被移动了,但之后可以添加相同数量的元素,而不会引起更多的移动,因为插入是在 vector 的末尾执行的)。
如果预先知道将在 vector 中插入多少个元素,那么可以首先调用 reserve() 方法将容量增加到至少指定的数量(如果指定的大小小于当前的容量,则该方法不执行任何操作),然后再插入元素。
如果需要释放额外的预留内存,那么可以使用 shrink_to_fit() 方法来请求释放,但是否释放内存是由实现决定的。这个非绑定方法的另一种替代方法(自 C++11 起可用)是用一个临时的空 vector 进行交换:
此外,调用 clear() 方法只会从 vector 中移除所有元素,但不会释放任何内存。
应该注意的是,vector 类实现了一些特定于其他类型容器的操作:
C++ 容器的一条经验法则是使用 std::vector 作为默认容器,除非有充分的理由使用其他容器。
标准库中的容器都是作为类模板实现的,这意味着它们可以与任意类型一起使用(只要它满足容器需求)。通常,应该始终使用最适合于特定问题的容器,这不仅在插入、删除、访问元素和内存使用速度方面提供了良好的性能,而且还使代码易于阅读和维护。
本节讲解 vector 容器的用法。
C++ vector的使用方式
类模板 vector 在头文件<vector>
的 std 命名空间中可用。要初始化 std::vector 类模板,可以使用以下方法中的任何一种(但不限于这些):
1) 从初始化列表进行初始化:
std::vector<int> v1 { 1, 2, 3, 4, 5 };
2) 从数组初始化:
int arr[] = { 1, 2, 3, 4, 5 }; std::vector<int> v21(arr, arr + 5); // v21 = { 1, 2, 3, 4, 5 } std::vector<int> v222(arr+1, arr+4); // v222 = { 2, 3, 4 }
3) 从另一个容器初始化:
std::list<int> l { 1, 2, 3, 4, 5 }; std::vector<int> v3(l.begin(), l.end()); // { 1, 2, 3, 4, 5 }
4) 从计数和值初始化:
std::vector<int> v4(5, 1); // {1, 1, 1, 1, 1 }
要修改 std::vector 的内容,可以使用以下方法中的任何一种(但不限于这些):
1) 使用 push_back() 在 vector 的末尾添加一个元素:
std::vector<int> v1{ 1, 2, 3, 4, 5 }; v1.push_back(6); // v1 = { 1, 2, 3, 4, 5, 6 }
2) 使用 pop_back() 从 vector 的末尾移除一个元素:
v1.pop_back();
3) 使用 insert() 函数在 vector 的任意位置插入元素:
int arr[] = { 1, 2, 3, 4, 5 }; std::vector<int> v21; v21.insert(v21.begin(), arr, arr + 5); // v21 = { 1, 2, 3, 4, 5 } std::vector<int> v22; v22.insert(v22.begin(), arr, arr + 3); // v2 = { 1, 2, 3 }
4) 通过使用 emplace_back() 方法在 vector 的末尾创建一个元素来添加一个元素:
struct foo { int a; double b; std::string c; foo(int a, double b, std::string const & c) : a(a), b(b), c(c) {} }; std::vector<foo> v3; v3.emplace_back(1, 1.0, "one"s); // v3 = { foo{1, 1.0, "one"} }
5) 通过使用 emplace() 方法在 vector 的任意位置创建一个元素来插入一个元素:
v3.emplace(v3.begin(), 2, 2.0, "two"s); // v3 = { foo{2, 2.0, "two"}, foo{1, 1.0, "one"} }
要修改 vector 的整个内容,可以使用以下方法中的任何一种(但不限于这些):
1) 用另外一个 vector 进行赋值(使用 operator=),这将替换容器的内容:
std::vector<int> v1{ 1, 2, 3, 4, 5 }; std::vector<int> v2{ 10, 20, 30 }; v2 = v1; // v2 = { 1, 2, 3, 4, 5 }
2) 通过 assign() 方法用 begin 和 end 迭代器定义的另一个序列进行赋值,这将替换容器的内容:
int arr[] = { 1, 2, 3, 4, 5 }; std::vector<int> v31; v31.assign(arr, arr + 5); // v31 = { 1, 2, 3, 4, 5 } std::vector<int> v32; v32.assign(arr + 1, arr + 4); // v32 = { 2, 3, 4 }
3) 使用 swap() 方法交换两个 vector 的内容:
std::vector<int> v4{ 1, 2, 3, 4, 5 }; std::vector<int> v5{ 10, 20, 30 }; v4.swap(v5); // v4 = { 10, 20, 30 }, v5 = { 1, 2, 3, 4, 5 }
4) 使用 clear() 方法删除所有元素:
std::vector<int> v6{ 1, 2, 3, 4, 5 }; v6.clear(); // v6 = { }
5) 使用 erase() 方法删除一个或多个元素(这需要一个迭代器或一对迭代器来定义 vector 中要删除的元素的范围):
std::vector<int> v7{ 1, 2, 3, 4, 5 }; v7.erase(v7.begin() + 2, v7.begin() + 4); // v7 = { 1, 2, 5 }
要获取 vector 中第一个元素的地址,通常需要将 vector 的内容传递给类 C API,可以使用以下方法中的任意一种:
1) 使用 data() 方法,它返回指向第一个元素的指针,提供对存储 vector 元素的底层连续内存块的直接访问,这只在 C++11 之后可用:
void process(int const * const arr, size_t const size) { /* do something */ } std::vector<int> v { 1, 2, 3, 4, 5 }; process(v.data(), v.size());
2) 获取第一个元素的地址:
process(&v[0], v.size());
3) 获取 front() 方法引用的元素的地址:
process(&v.front(), v.size());
4) 获取由 begin() 返回的迭代器所指向的元素的地址:
process(&*v.begin(), v.size());
深度解读vector
std::vector 类被设计成与数组最相似且可与数组互操作的 C++ 容器。vector 是一个大小可变的元素序列,保证连续地存储在内存中,这使得 vector 的内容可以很容易地传递给一个类 C 的函数——该函数接受一个指向数组元素的指针(通常是一个大小)。使用 vector 代替 array 有很多好处,包括:
- 开发人员不需要直接进行内存管理,因为容器在内部完成分配、重新分配和释放内存的工作;
- 有修改 vector 大小的可能性;
- 可进行两个 vector 的简单赋值或连接;
- 可进行两个 vector 的直接比较。
注意,vector 用于存储对象实例。如果需要存储指针,请不要存储原始指针,而要存储智能指针。否则,需要管理指向对象的生命周期。
vector 类是一个非常高效的容器,它的所有实现都提供了许多优化,而大多数开发人员无法对 array 进行这些优化。对其元素的随机访问以及在 vector 末尾的插入和删除是常数 0(1) 操作(前提是不需要重新分配内存),而在其他任何地方的插入和删除是线性 0(n) 操作。
与其他标准容器相比,vector 具有很多优点:
- 它与 array 和类 C API 兼容:如果函数接受数组作为形参,则需要将其他容器(除了 std::array)的内容在作为实参传递给函数之前复制到 vector 中;
- 它对所有容器的元素的访问速度最快(但和 std::array 一样);
- 它没有用于存储元素的每个元素内存开销,这是因为元素像数组一样存储在连续空间中。因此,vector 占用较少的内存,这不同于其他容器(比如 list)(需要附加指向其他元素的指针)或关联容器(需要哈希值)。
std::vector 在语义上与数组非常相似,但其大小是可变的,可以增大,也可以减小。以下两个属性定义了 vector 的大小:
- 容量是指 vector 在不执行额外内存分配的情况下能够容纳的元素数量,这是由 capacity() 方法确定的;
- 大小是 vector 中元素的实际数量,这是由 size() 方法确定的。
大小总是小于或等于容量,当大小等于容量时,并且需要添加一个新元素时,就需要修改容量,以便 vector 有容纳更多元素的空间。在这种情况下,vector 会分配一个新的内存块,并在释放已分配的内存之前将已有的内容移动到新的内存块。虽然这听起来很耗费时间,事实上也确实如此,但在每次需要更改时,vector 的实现都会成倍数地增加容量。平均而言,vector 的每个元素只需要移动一次(这是因为在增加容量的过程中,vector 的所有元素都被移动了,但之后可以添加相同数量的元素,而不会引起更多的移动,因为插入是在 vector 的末尾执行的)。
如果预先知道将在 vector 中插入多少个元素,那么可以首先调用 reserve() 方法将容量增加到至少指定的数量(如果指定的大小小于当前的容量,则该方法不执行任何操作),然后再插入元素。
如果需要释放额外的预留内存,那么可以使用 shrink_to_fit() 方法来请求释放,但是否释放内存是由实现决定的。这个非绑定方法的另一种替代方法(自 C++11 起可用)是用一个临时的空 vector 进行交换:
std::vector<int> v{ 1, 2, 3, 4, 5 }; std::vector<int>().swap(v); // v.size = 0, v.capacity = 0
此外,调用 clear() 方法只会从 vector 中移除所有元素,但不会释放任何内存。
应该注意的是,vector 类实现了一些特定于其他类型容器的操作:
- stack:使用 push_back() 和 emplace_back() 在末尾添加元素,使用 pop_back() 从末尾移除元素。请记住,pop_back() 不会返回被删除的最后一个元素,如果有必要,你需要显式地访问它,方法是在删除元素之前使用 back() 方法。
- list:使用 insert() 和 emplace() 在序列中间添加元素,使用 erase() 从序列中的任意位置删除元素。
C++ 容器的一条经验法则是使用 std::vector 作为默认容器,除非有充分的理由使用其他容器。