参考:C++ vector链接:http://www.cplusplus.com/reference/vector/vector/
1.vector
vector 是 C++ STL 中一种顺序容器(sequence container),其底层实现基于动态数组。与普通数组不同的是,vector 可以根据需要动态扩展其大小,即它能够存储任意数量的元素,而不需要在创建时指定一个固定的大小。
2 vector 的优点
a.动态扩展
vector 能够自动调整大小,避免了固定大小数组带来的内存不足问题。
b.高效的随机访问
由于 vector 底层是连续的内存块,因此它可以像数组一样通过索引进行快速的随机访问。
c.灵活的插入和删除
vector 支持高效的尾部插入和删除操作,且提供了多种插入、删除方式。
d.STL 兼容性
vector 是 STL 容器,支持 STL 的算法和迭代器,可以与其他 STL 容器和算法无缝结合。
3 vector 的缺点
a.头部和中间的插入、删除效率低:由于 vector 使用连续的内存块,因此在中间或头部插入或删除元素时,需要移动大量元素,时间复杂度为 O(n)
b.内存浪费:为了提高扩展效率,vector 通常会预留比实际需要更多的内存,这可能导致内存浪费。
4 vector 的定义
#include <vector>
std::vector<int> vec; // 定义一个存储整数的空vector
std::vector<int> vec(10); // 定义一个包含10个元素的vector,元素值默认初始化为0
std::vector<int> vec(10, 1); // 定义一个包含10个元素的vector,元素值为1
std::vector<int> vec2 = vec; // 定义一个新vector,并通过拷贝构造函数初始化
5 基本操作
push_back():在 vector 的末尾插入元素。
pop_back():删除 vector 末尾的元素。
size():返回 vector 中元素的数量。
capacity():返回 vector 当前的容量,即它在不重新分配内存的情况下最多可以容纳的元素数。
clear():清空 vector 中的所有元素。
empty():判断 vector 是否为空。
resize():调整 vector 的大小。
reserve():为 vector 预留一定的容量,避免频繁的重新分配内存。
shrink_to_fit():清理没有使用的空间
6 迭代器的使用
begin()
end()
rbegin()
rend()
7 vector 动态数组的实现
vector 的底层实现基于动态数组。当需要插入新元素时,如果当前容量不足,vector 会自动分配更大的内存块,并将原来的元素拷贝到新的内存块中。这种动态扩展策略的时间复杂度较低,因为 vector 的容量在每次扩展时通常是当前容量的两倍
std::vector<int> vec;
vec.push_back(1); // 第一次插入,分配内存
vec.push_back(2); // 插入,内存足够,不需要重新分配
vec.push_back(3); //
// 当容量不足时,vector 会重新分配内存,通常是原来容量的两倍。
8 vector 内存管理
vector 通过 capacity 来控制当前分配的内存大小,而 size 表示实际存储的元素数量。capacity 总是大于或等于 size。当 size 超过 capacity 时,vector 会重新分配内存,并将所有现有元素拷贝到新的内存地址。
std::vector<int> vec;
vec.reserve(10); // 预先分配10个元素的内存
vec.push_back(1); // 不会触发内存重新分配
vec.push_back(2); // 不会触发内存重新分配
9 vector 插入元素
vec.push_back(1); // 尾部插入1,时间复杂度 O(1)
vec.pop_back(1); // 移除尾部,时间复杂度 O(1)
vec.insert(vec.begin() + 1, 10); // 在第二个位置插入10,后面的元素都需要向后移动,O(n)
vec.erase(vec.begin() + 2); // 删除第三个元素,后面的元素都需要向前移动,O(n)
10 vector 拷贝复制和move
// 当一个 vector 被赋值或复制时,会创建一个新对象,并将所有元素进行深拷贝。这意味着修改新对象不会影响原来的 vector。
std::vector<int> vec1 = {1, 2, 3};
std::vector<int> vec2 = vec1; // 深拷贝
vec2[0] = 10;
std::cout << vec1[0]; // 输出1,vec1不受影响// C++11 引入了移动语义,允许将 vector 的资源直接转移到另一个对象,而不需要进行深拷贝。这可以极大地提高性能,尤其是在处理大型对象时。
std::vector<int> vec1 = {1, 2, 3};
std::vector<int> vec2 = std::move(vec1); // 使用std::move,vec1的资源转移给vec2
11 emplace_back() 与 push_back() 的区别
emplace_back() 是 C++11 新增的功能,它允许直接在容器的末尾构造对象,而无需先构造对象再拷贝到 vector。相比之下,push_back() 会进行额外的拷贝操作,因此在某些情况下,emplace_back() 会比 push_back() 更高效。
class Person {
public:Person(const std::string& name, int age) : name(name), age(age) {}
private:std::string name;int age;
};// 使用push_back
std::vector<Person> people;
people.push_back(Person("John", 30)); // 先构造Person对象,然后拷贝到vector中// 使用emplace_back
people.emplace_back("Jane", 25); // 直接在vector内部构造Person对象,避免拷贝
12 shrink_to_fit() 减少容量浪费
由于 vector 通常会预留比实际所需更多的内存空间(capacity()),可能会造成内存浪费。shrink_to_fit() 函数用于释放未使用的内存,使得 vector 的容量等于其大小。
std::vector<int> vec = {1, 2, 3};
vec.reserve(10); // 预留10个元素的空间
std::cout << vec.capacity(); // 输出10vec.shrink_to_fit();
std::cout << vec.capacity(); // 输出3,容量被调整为实际使用的大小
13 vector 常见陷阱
a.容量浪费问题
vector 的容量通常大于其实际存储的元素数量。如果程序中频繁进行插入操作且对内存使用敏感,可以使用 shrink_to_fit() 来减少浪费。
b.迭代器失效(最需要注意的地方)
在 vector 中进行插入或删除操作时,所有指向 vector 元素的迭代器、指针或引用可能会失效。这是因为插入或删除操作可能导致 vector 重新分配内存,从而改变所有元素的地址。
std::vector<int> vec = {1, 2, 3};
auto it = vec.begin();
vec.push_back(4); // 可能导致内存重新分配
std::cout << *it; // 迭代器可能失效,行为未定义
c.元素的析构
当 vector 中的对象被删除时,会调用对象的析构函数。因此,如果 vector 存储的是指针类型,在删除 vector 或清空元素时需要特别小心,确保不会引发内存泄漏。
std::vector<int*> vec;
vec.push_back(new int(10));
vec.clear(); // 仅删除了指针,但没有释放内存,可能导致内存泄漏
解决方案是在删除元素之前手动释放内存,或者使用智能指针。