std::vector 的底层存储机制是一个动态数组,它内部通过一片连续的内存空间来存储元素。当这个连续的内存空间不足以容纳新元素时,std::vector 会自动申请一块更大的内存空间,通常是当前容量的 1.5 倍或 2 倍,然后将原有数据拷贝到新的内存空间,并释放原来的内存空间。这个过程称为 reallocation。
std::vector 内部有三个基本的指针,分别是 start、finish 和 end_of_storage。start 指向容器的首元素,finish 指向尾元素的下一个位置,而 end_of_storage 指向容器的最大位置。这三个指针帮助 vector 实现了许多功能,如已存储元素大小、剩余空间大小、总容器空间大小等等。
需要注意的是,std::vector 的扩容机制时间成本较高,因此在实际使用中,如果能提前确定容器空间的大小,最好提前设定好容器空间的大小,以避免频繁的扩容操作。同时,由于扩容可能会导致原有指针、迭代器失效,因此在扩容后需要特别注意指针和迭代器的有效性。
此外,std::vector 还支持随机访问,因此访问某一个元素的时间复杂度是 O(1)。但是,由于其内部是连续的内存空间,所以在插入到非尾结点位置或删除元素时,需要移动的元素数量较多,时间复杂度为 O(n)。这也是 std::vector和std::list 等其他容器在性能上的一个主要区别。
std::vector 的自增长机制是通过动态调整其内部存储空间的容量来实现的。当 std::vector 需要插入新元素,而当前的存储空间不足以容纳时,它就会触发自增长机制。
自增长机制的实现过程大致如下:
(1)检查容量: 当需要插入新元素时,std::vector 会首先检查当前的容量(capacity)是否足够。容量是指 vector 在内存中预留的空间大小,它至少应该等于 vector 的大小(size),也就是当前存储的元素数量。
(2)重新分配内存: 如果容量不足,std::vector 会重新分配内存空间。通常情况下,它会分配一个更大的内存块,通常是当前容量的1.5倍或2倍(具体倍数可能因实现而异)。这样做是为了减少未来插入元素时再次触发重新分配的次数。
(3)数据迁移: 然后,std::vector会将原有数据(即所有已存储的元素)从旧的内存空间复制到新的内存空间。这是一个相对耗时的操作,因为它涉及到内存拷贝。
(4)释放旧内存: 在数据迁移完成后,std::vector会释放原来的内存空间,这样旧的内存就可以被操作系统或其他数据结构重新利用了。
(5)更新指针: 最后,std::vector 会更新其内部的指针,使 start 和 finish 指针指向新的内存空间中的正确位置,end_of_storage 指针则指向新的内存空间的末尾。
需要注意的是,std::vector 的自增长机制可能会导致一些性能问题。因为每次重新分配内存和数据迁移都是一个相对耗时的操作,特别是在插入大量元素时。因此,在实际应用中,如果可能的话,最好预先知道或估计 vector 可能需要存储的元素数量,并使用 reserve() 函数提前为其分配足够的空间,以减少重新分配的次数。
std::vector和std::array在C++中都是容器,用于存储一系列相同类型的对象,但它们之间存在一些关键的区别:
(1)动态与静态:
(2)访问方式:
(3)初始化和使用:
(4)适用场景:
(5)异常安全性:
总的来说,std::vector 和 std::array 都有各自的优点和适用场景,选择使用哪一个取决于你的具体需求,比如是否需要动态调整大小、对性能的要求、以及是否已知数组的大小等。
在 std::vector 容器中,迭代器失效主要发生在以下情况:
当 std::vector 容器进行扩容时,即当现有容量不足以容纳新元素时,std::vector 会重新分配内存空间,并将原有元素复制到新的内存空间中。这个过程可能会导致原有迭代器、引用和指针失效。
当 std::vector 容器的元素被删除时,指向被删除元素的迭代器也会失效。
下面是一个迭代器失效的例子:
#include
#include
int main()
{
std::vector<int> vec = { 1, 2, 3, 4, 5 };
// 获取指向vector第一个元素的迭代器
std::vector<int>::iterator it = vec.begin();
// 在迭代器指向的元素之前插入一个新元素
vec.insert(it, 0);
// 此时,it指向的元素已经被新插入的元素覆盖,it已经失效
// 如果继续使用it,可能会导致未定义的行为,如程序崩溃或数据错误
// 尝试打印it指向的元素,这将导致迭代器失效的错误
std::cout << "The element at iterator position is: " << *it << std::endl;
return 0;
}
在上面代码中,首先创建了一个包含 5 个元素的 std::vector。然后获取了一个指向 vector 第一个元素的迭代器it。接下来,在 it 指向的元素之前插入了一个新的元素。由于这个插入操作导致 vector 扩容(如果原容量不足以容纳新元素),vector 重新分配了内存,并将原有元素复制到新内存空间。这时,原有的迭代器 it 已经失效,因为它指向的内存位置现在包含了一个新插入的元素。
如果尝试使用失效的迭代器 it 来访问元素,比如通过 *it 来解引用它,将会导致未定义的行为。在实际应用中,这可能会导致程序崩溃,或者更难以追踪的数据错误。
因此,在编程时必须格外注意避免在迭代器失效后继续使用它。当对 std::vector 容器进行修改操作(如插入、删除、扩容等)时,任何指向容器中元素的迭代器、引用和指针都可能失效。在这种情况下,应该重新获取迭代器,或者确保在修改操作之前或之后不使用这些迭代器。
std::vector 和 std::list 是 C++ 标准模板库 (STL) 中的两种非常重要的序列式容器,它们有着显著的区别。以下是它们之间的主要区别:
(1)底层实现与内存管理:
(2)随机访问性能:
std::vector:支持随机访问,即可以通过下标直接访问任何位置的元素,时间复杂度为 O(1)。
std::list:不支持随机访问,访问元素需要从头或尾开始遍历,时间复杂度为 O(n)。
(3)插入与删除操作:
std::vector:在任意位置插入或删除元素时,可能需要移动后续的所有元素来填补空缺,时间复杂度为 O(n)。
std::list:插入或删除元素只需要改变节点指针,时间复杂度为 O(1)。
(4)空间利用率与缓存效率:
std::vector:由于其元素是连续存储的,不容易造成内存碎片,空间利用率和缓存效率较高。
std::list:由于其使用链表结构,每个节点可能不是连续存储的,容易造成内存碎片,导致空间利用率和缓存效率较低。
(5)迭代器:
std::vector:迭代器是原生态的指针。
std::list:迭代器是对原生态指针进行了封装。
(6)迭代器失效问题:
std::vector:在插入元素时,如果导致扩容,原有迭代器可能会失效。删除元素时,被删除元素及其之后的迭代器也会失效。
std::list:插入元素不会导致迭代器失效,而删除元素只会导致当前迭代器失效,其他迭代器不受影响。
(7)使用场景:
std::vector:适用于需要高效存储和随机访问的场景,不关心插入效率。
std::list:适用于大量插入和删除操作的场景,不关心随机访问。
总结,std::vector 和 std::list 的选择取决于具体需求。如果需要高效的随机访问和连续的内存存储,std::vector 是更好的选择。如果需要频繁地插入和删除元素,而不太关心随机访问,那么 std::list 可能更适合。
在C++的std::vector
中插入和删除元素有多种方法,下面是一些常用的方法:
插入元素
(1)使用 insert 成员函数
insert 函数可以在指定位置插入一个或多个元素。
#include
#include
int main()
{
std::vector<int> vec = {1, 2, 4, 5};
// 在位置2插入元素3
vec.insert(vec.begin() + 2, 3);
// 输出vector的内容
for (int num : vec) {
std::cout << num << " ";
}
std::cout << std::endl;
return 0;
}
上面代码的输出为:
1 2 3 4 5
insert 也可以接受一个迭代器范围来插入多个元素。
std::vector<int> to_insert = {3, 3, 3};
vec.insert(vec.begin() + 2, to_insert.begin(), to_insert.end());
(2)使用 push_back 成员函数
push_back 在vector的末尾添加一个新元素。
vec.push_back(1);
(3)使用 emplace 或 emplace_back 成员函数
emplace 和 emplace_back 可以在指定位置就地构造一个新元素,这通常比先构造元素再插入更高效。
vec.emplace(vec.begin() + 2, 3); // 在位置2就地构造并插入元素3
vec.emplace_back(7); // 在vector末尾就地构造并插入元素7
删除元素
(1)使用 erase 成员函数
erase 可以删除一个或多个元素。
// 删除位置2的元素
vec.erase(vec.begin() + 2);
// 删除从位置1到位置3的元素(不包括位置3)
vec.erase(vec.begin() + 1, vec.begin() + 3);
(2)使用 pop_back 成员函数
pop_back 删除vector的最后一个元素。
if (!vec.empty()) {
vec.pop_back();
}
(3)使用 clear 成员函数
clear 删除vector中的所有元素。
vec.clear(); // vector现在为空
注意:当使用 insert 或 erase 时,指向被修改部分的迭代器、引用和指针可能会失效。因此,在插入或删除元素后,应该避免使用这些失效的迭代器、引用和指针。
此外,频繁地在 std::vector 中间位置插入或删除元素可能会导致性能下降,因为可能需要移动大量的元素来保持连续性。如果这种操作很频繁,考虑使用 std::list 或 std::deque 等容器可能会更有效。