c++ vector数组详细介绍(一)https://developer.aliyun.com/article/1436994
直接修改元素
你可以使用下标运算符 [] 或 at() 方法来直接访问并修改 vector 中的元素。这种方法类似于操作普通数组。
std::vector<int> v = {10, 20, 30, 40}; v[2] = 100; // 使用下标运算符修改第三个元素 v.at(3) = 200; // 使用 at() 方法修改第四个元素
添加元素
push_back(value): 在 vector 的末尾添加一个新元素。
emplace_back(args…): 类似于 push_back,但可以直接构造元素,避免复制或移动操作。
insert(position, value): 在指定位置插入一个或多个新元素。
std::vector<int> v = {10, 20, 30}; v.push_back(40); // 现在 v = {10, 20, 30, 40} v.emplace_back(50); // 现在 v = {10, 20, 30, 40, 50} v.insert(v.begin() + 2, 15); // 在第三个位置插入 15,现在 v = {10, 20, 15, 30, 40, 50}
删除元素
pop_back(): 移除 vector 末尾的元素。
erase(position) 或 erase(start, end): 移除指定位置或范围内的元素。
clear(): 清空整个 vector。
std::vector<int> v = {10, 20, 30, 40, 50}; v.pop_back(); // 移除最后一个元素,现在 v = {10, 20, 30, 40} v.erase(v.begin() + 2); // 移除第三个元素,现在 v = {10, 20, 40} v.clear(); // 清空整个 vector
其他修改操作
resize(n, value): 改变 vector 的大小。如果新大小大于当前大小,新元素将被初始化为 value(如果提供了的话)。
swap(vector): 与另一个 vector 交换内容。
std::vector<int> v1 = {1, 2, 3}; std::vector<int> v2 = {4, 5, 6}; v1.resize(5, 100); // 现在 v1 = {1, 2, 3, 100, 100} v1.swap(v2); // 现在 v1 = {4, 5, 6}, v2 = {1, 2, 3, 100, 100}
注意事项
在使用 [] 或 at() 修改元素时,确保索引在 vector 的有效范围内。
在添加或删除元素时,vector 的大小会自动调整。
使用 emplace_back 而非 push_back 可以提高效率,尤其是对于复杂类型的对象。
insert, erase, 和 resize 等操作可能影响 vector 的性能,特别是在处理大量数据时。
2.5 迭代器
迭代器类型
正向迭代器:
允许你从 vector 的开头向末尾遍历。
反向迭代器:
允许你从 vector 的末尾向开头遍历。
常量迭代器:
不允许修改它们所指向的元素,可以是正向或反向。
获取迭代器
使用 begin() 和 end() 成员函数获得指向 vector 开头和末尾的正向迭代器。
使用 rbegin() 和 rend() 获得反向迭代器。
cbegin()、cend()、crbegin() 和 crend() 分别用于获取常量正向和反向迭代器(C++11 及以后版本)。
迭代器操作
遍历:通过递增(++)和递减(- -)迭代器来遍历 vector。
访问元素:使用解引用操作符(*)来访问迭代器所指向的元素。
比较:迭代器可以使用比较操作符(如 == 和 !=)来判断是否到达 vector 的末尾或特定位置。
#include <vector> #include <iostream> int main() { std::vector<int> v = {1, 2, 3, 4, 5}; // 使用正向迭代器遍历 for (auto it = v.begin(); it != v.end(); ++it) { std::cout << *it << " "; } std::cout << std::endl; // 使用反向迭代器遍历 for (auto rit = v.rbegin(); rit != v.rend(); ++rit) { std::cout << *rit << " "; } std::cout << std::endl; return 0; }
注意事项
在使用迭代器时,确保不要越界访问。例如,不要解引用 end() 或 rend() 返回的迭代器。
修改 vector(如添加或删除元素)可能会使现有迭代器失效。在进行这类操作后,应该重新获取迭代器。
常量迭代器适用于不需要修改元素值的情况,增强代码的安全性。
通过使用迭代器,你可以编写更通用的代码,这些代码可以适用于多种不同的容器类型,不仅仅是 vector。
3. vector大小和容量详解
在 C++ 中,vector 的大小(size)和容量(capacity)是两个重要但不同的概念。理解这两者之间的区别对于有效地使用 vector 来说非常关键。
大小(Size)
定义:
vector 的大小是指它当前包含的元素数量。
获取方法:
使用 size() 成员函数。
动态变化:
当你添加或删除元素时,vector 的大小会相应增加或减少。
示例:
如果你向一个空的 vector 添加了三个元素,它的大小(size())将是 3。
容量(Capacity)
定义:
vector 的容量是指在需要重新分配内存之前它可以容纳的元素数量。
获取方法:
使用 capacity() 成员函数。
预先分配:
为了减少多次分配内存的开销,vector 通常会预先分配比当前大小更多的空间。这意味着容量通常会等于或大于大小。
自动增长:
当添加新元素导致当前容量不足时,vector 会自动增加其容量(通常是当前容量的两倍,但这取决于实现)。
示例:
一个大小为 3 的 vector 可能有一个容量为 6,表示它可以在不重新分配内存的情况下增加到 6 个元素。
重要操作
resize(n): 改变 vector 的大小为 n。如果 n 大于当前大小,会添加默认初始化的元素;如果 n 小于当前大小,多余的元素会被移除。
std::vector<int> v = {1, 2, 3}; v.resize(5); // 大小改为 5,添加了两个值为 0 的元素 reserve(n): 保证 vector 至少有 n 的容量。如果 n 大于当前容量,会进行内存分配以增加容量;否则,该方法不会改变 vector 的容量。
std::vector<int> v; v.reserve(10); // 容量至少为 10,但大小仍为 0
注意事项
对于性能敏感的应用,合理管理 vector 的大小和容量是很重要的。频繁的内存分配和复制可能会导致性能问题。
使用 reserve() 预分配内存可以减少因添加新元素而导致的多次内存重新分配。
size() 和 capacity() 的值可以相同,但也可以不同。capacity() 永远不会小于 size()。
在处理大量数据时,了解和合理利用 vector 的大小和容量可以帮助优化程序的性能和内存使用。
4. 算法操作
C++ 中的 vector 容器与标准模板库(STL)提供的算法高度兼容。这种兼容性是通过迭代器实现的,使得 vector 可以与大多数通用算法一起使用。
算法概述
STL 算法库包括一系列标准化的函数,用于执行范围内的操作,如排序、搜索、转换、计数等。
这些算法通常通过一对迭代器来指定作用的范围(例如,begin() 和 end())。
常用算法示例
排序 (sort):
对 vector 中的元素进行排序。
std::vector<int> v = {4, 1, 3, 5, 2}; std::sort(v.begin(), v.end()); // 升序排序
查找 (find):
查找 vector 中的特定元素。
auto it = std::find(v.begin(), v.end(), 3); if (it != v.end()) { std::cout << "Element found!" << std::endl; }
反转 (reverse):
反转 vector 中的元素顺序。
std::reverse(v.begin(), v.end());
累加 (accumulate):
计算 vector 中所有元素的总和。
int sum = std::accumulate(v.begin(), v.end(), 0);
唯一化 (unique):
移除连续且相同的元素。
std::sort(v.begin(), v.end()); // unique 需要先排序 auto last = std::unique(v.begin(), v.end()); v.erase(last, v.end()); // 移除重复之后的多余元素
注意事项
在使用这些算法时,确保传递正确的迭代器范围。
某些算法(如 sort 和 unique)可能改变容器中元素的位置或数量。在使用这些算法之后,之前获取的迭代器可能会失效。
许多算法需要容器中的元素已经按照某种顺序排列,例如 unique 和 binary_search,在使用这些算法之前,需要确保容器已经正确排序。
accumulate 等算法可能需要额外的头文件(如 )。
通过将 vector 与 STL 算法结合使用,你可以编写出既简洁又高效的代码来处理复杂的数据集操作。这也是 C++ STL 强大和灵活的一个重要体现。
5. 性能考量
当使用 C++ 的 vector 容器时,考虑其性能特点是非常重要的。虽然 vector 提供了便利性和灵活性,但在某些情况下,它的行为可能对程序的性能产生影响。以下是需要考虑的主要性能方面:
5.1 动态数组的性能影响
内存分配和重新分配:
当向 vector 添加新元素而当前容量不足以容纳它们时,vector 需要进行内存重新分配。这个过程涉及分配新的更大的内存块、复制现有元素到新内存、并释放旧内存。这可能是一个相对昂贵的操作,特别是对于大型 vector 或包含复杂对象的 vector。
预分配容量:
通过 reserve() 方法预先分配足够的容量可以避免或减少内存重新分配的次数,从而提高性能。
5.2 元素访问和修改
随机访问:
vector 提供了快速的随机访问(即直接通过索引访问),这在性能方面是一个优势,特别是与一些其他容器(如 list 或 forward_list)相比。
插入和删除:
在 vector 的末尾添加或移除元素(例如使用 push_back() 和 pop_back())是非常快速的。但是在 vector 的开始或中间位置插入或删除元素可能会导致现有元素的移动,这可能是一个较慢的操作。
5.3 大小与容量管理
容量与大小:
vector 的容量可能大于其实际大小。这意味着 vector 可能会占用比实际需要的更多内存。使用 shrink_to_fit() 可以请求减少 vector 的容量以匹配其大小,尽管这种请求不一定总会被满足。
大小变更操作:
resize() 和 reserve() 可以用来管理 vector 的大小和容量。合理使用这些操作可以避免不必要的内存重新分配。
5.4 迭代器和算法
迭代器遍历:
使用迭代器遍历 vector 通常是高效的。但是,如果在遍历过程中修改 vector(例如添加或删除元素),可能会使迭代器失效,导致未定义行为。
5.5 总体性能
整体效率:
总的来说,vector 是一个高效的容器,特别是在需要快速随机访问元素时。对于大多数用途,它提供了良好的性能和方便的接口。
选择合适的容器:
虽然 vector 在很多情况下都是一个好的选择,但根据具体需求选择合适的容器类型是很重要的。例如,如果你需要频繁在容器中间插入或删除元素,list 或 deque 可能是更好的选择。
6. 高级特性
C++ vector 容器提供了一些高级特性,特别是在 C++11 和后续版本中。这些特性不仅增加了灵活性,还改善了性能和使用方便性。以下是一些重要的高级特性:
初始化列表 (C++11)
允许使用花括号 {} 初始化 vector,使代码更简洁。
例如:
std::vector<int> v = {1, 2, 3, 4, 5};
emplace_back 和 emplace (C++11)
这些函数在 vector 中直接构造元素,而不是先构造然后复制或移动到容器中。
这可以提高性能,特别是对于包含复杂对象的 vector。
例如:
v.emplace_back(3, "three"); //在 vector 的末尾直接构造一个元素。
范围构造器 (C++11)
允许使用两个迭代器作为参数来构造 vector,从而复制或移动另一个容器中的元素范围。
例如:
std::vector<int> v1 = {1, 2, 3}; std::vector<int> v2(v1.begin(), v1.end());
shrink_to_fit (C++11)
请求减少 vector 的容量以匹配其大小,有助于节省内存。
不保证容量一定会减少,但大多数实现都会尽量满足这个请求。
data 方法 (C++11)
提供对 vector 内部数组的直接访问。
可以用于与 C 风格的函数接口交互,需要一个指向数组的指针。
例如:
int* p = v.data();
移动语义 (C++11)
vector 支持移动构造和移动赋值,这意味着在某些情况下可以避免复制整个 vector 的内容,而是直接移动它。
对于包含大量元素的 vector,这可以大大提高性能。
noexcept 修饰符 (C++11)
一些 vector 操作被标记为 noexcept,表明它们不会抛出异常。
这对于某些需要异常安全保证的场景非常有用。
可以在 vector 上使用的 Lambda 表达式 (C++11)
结合 STL 算法,可以用于强大的元素处理,如 std::for_each 或 std::transform。
支持非标准分配器
vector 可以使用自定义分配器,这对于特殊的内存管理需求很有用。
这些高级特性使得 vector 更加强大和灵活,同时也提高了其与现代 C++ 语言特性的兼容性。这些特性的使用可以使代码更加高效、简洁和功能强大。
7. vector动态大小详解
vector 可以根据需要自动调整大小,这意味着你可以在运行时向 vector 添加或删除元素,而它的大小会自动增长或缩减。
vector 的动态大小特性是其最重要的特点之一。这个特性允许 vector 在运行时根据需要自动调整其存储的元素数量。以下是对这一特性的详细介绍:
自动调整大小
当新元素被添加到 vector 中,而当前的存储空间不足以容纳更多元素时,vector 会自动扩展其容量。
通常,vector 会将其容量增加到当前大小的两倍,这种增长策略旨在平衡内存使用和重新分配的次数。不过,具体的增长策略可能因实现而异。
内存管理
vector 在内部管理一个动态数组,用于存储其元素。当 vector 的大小改变时,它可能需要分配一个更大的数组并将现有元素复制到新数组中,然后释放旧数组的内存。
这个过程是自动的,对使用者来说是透明的。
c++ vector数组详细介绍(三)https://developer.aliyun.com/article/1437019?spm=a2c6h.13262185.profile.51.5bba685cuSQkDD