【基础知识】vector

目录

1 vector的基础知识

2 vector的底层实现原理

3 vector中的reserve和resize的区别

4 vector中的size和capacity的区别

5 vector的元素类型可以时引用吗?

6 vector迭代器失效的情况


1 vector的基础知识

        在 C++ 中,vector是一个标准库容器类,用于表示动态数组,也被称为可变长数组。vector 容器可以存储任意类型的元素,支持随机访问和动态调整大小等操作,是 C++ 中最常用的容器之一。

        vector 内部实现使用动态数组,也就是在内存中申请一块连续的空间,存储元素,并提供了相应的方法来管理数组大小和元素的存取。由于是动态数组,因此可以根据需要动态增加或减少元素,不需要在使用前就确定数组大小。

        vector 使用前需要包含头文件‘<vector>’,然后可以通过以下方式定义一个 vector 对象:

#include<vector>
using namespace std;

vector<int> vec;//定义一个存储int类型元素的vector

        可以使用‘push_back()’方法向 vector 尾部添加元素,例如:

vec.push_back(1);    //在尾部添加元素1
vec.push_back(2);    //在尾部添加元素2
vec.push_back(3);    //在尾部添加元素3

        可以通过下标访问元素,例如:

cout << vec[0] << endl; // 输出 1
cout << vec[1] << endl; // 输出 2
cout << vec[2] << endl; // 输出 3

vector 还支持很多常用的操作:

  • size() :返回 vector 中元素的个数
  • empty() :检查 vector 是否为空
  • pop_back() :从 vector 尾部删除一个元素
  • insert() :在 vector 中的指定位置插入一个元素
  • erase() :从 vector 中的指定位置删除一个元素
  • clear() :清空 vector 中的所有元素

2 vector的底层实现原理

        vector 容器是一个动态数组,可以自动扩容和缩容,具有高效的随机访问和尾部插入/删除元素的特点。vector 的底层实现原理是基于数组实现的,vector 使用一个指向连续内存块的指针来存储元素,数组的大小会根据需要自动增加或减少。当 vector 需要扩容时,它会申请一段更大的连续内存块,并将现有元素复制到新的内存块中,然后释放旧内存块。这个过程可以通过成倍增长的方式来减少内存分配的次数,从而提高效率。

        在 vector 的实现中,通常会预留一部分内存空间,以避免频繁的重新分配。当 vector 中的元素数量达到预留的空间时,vector 会再次自动分配更大的内存空间,以便继续存储更多的元素。

        vector 的元素可以通过索引进行访问,这是由于 vector 的元素在内存中是连续存储的,因此可以通过指针偏移的方式快速定位到指定的元素。在插入或删除元素时,vector 会将其它元素的位置进行调整,以确保元素仍然连续存储。

        vector 底层是一个动态数组,包含三个迭代器,startfinish 之间是已经被使用的空间范围,end_of storage 是整块连续空间包括备用空间的尾部。当空间不够装下数据(vec.push_back(val))时,会自动申请另一片更大的空间(1.5 倍或者 2 倍),然后把原来的数据拷贝到新的内存空间,接着释放原来的那片空间【vector 的内存增长机制】。

        当释放或者删除(vec.clear())里面的数据时,其存储空间不释放,仅仅是清空了里面的数据。因此,对 vector 的任何操作一旦引起了空间的重新配置,指向原 vector 的所有迭代器会都失效了。

3 vector中的reserve和resize的区别

(1)reserve

        reserve 是容器预留空间,以便在向其中添加元素时,不需要重新分配内存,但并不真正创建元素对象,在创建元素对象之前,不能引用容器内的元素。它并不会改变 vector 中的元素个数,只会改变 vector 的容量。具体来说,当我们调用 reserve 函数并传入一个参数时,vector 会为其分配指定数量的内存空间,以便在未来向其中添加元素时可以直接使用该内存空间,而不必再进行内存分配。

        当我们向 vector 中添加元素时,其大小会增加,而当元素数量超过当前容量时,vector 会重新分配内存,将其容量扩大一定的倍数(通常是原来容量的两倍),以便存储更多的元素。这个过程可能会很耗时,因此可以在必要时使用 reserve() 函数来预先分配 vector 的容量。

std::vector<int> vec;
vec.reserve(10);//预留10个元素的空间

        上述代码中,我们使用 reserve 函数来预留 vector 可以存储 10 个元素的内存空间,这意味着,即使我们现在并没有向 vector 中添加任何元素,它也已经分配了足够的内存空间以容纳 10 个元素。这样,在向 vector 中添加元素时,就可以直接使用该内存空间,而不必再进行内存分配,从而提高程序的效率。

(2)resize

        resize 函数是用于改变 vector 中元素的数量的。具体来说,当我们调用 resize 函数并传入一个参数时,vector 会将其元素数量修改为指定的大小,如果现有元素个数小于指定大小,则会插入新元素;如果现有元素个数大于指定大小,则会删除一些元素。

std::vector<int> vec;
vec.resize(10);//调整vector的大小为10

        上述代码中,我们使用 resize 函数来将 vector 中元素的数量修改为 10,如果 vector 中现有元素的个数小于 10,则会在尾部插入足够多的元素,使得 vector 中的元素个数达到 10;如果 vector 中现有元素的个数大于 10,则会从尾部删除一些元素,使得 vector 的元素个数为 10.

(3)区别

  • reserve
    • reserve 是直接扩充到已经确认的大小,可以减少多次开辟、释放空间的问题(优化 push_back)就可以提高效率,其次还可以减少多次要拷贝数据的问题。
    • reserve 只是保证 vector 中的空间大小(capacity)最少达到参数所指定的大小 n
    • reserve( ) 只有一个参数
  • resize
    • resize( ) 可以改变有效空间的大小,也有改变默认值的功能
    • capacity 的大小也会随着改变
    • resize( ) 可以有多个参数

(4)例子

int main()
{
    vector<int> vecSize;
    vector<int> vecServe;

    //resize
    vecSize.resize(2);
    cout << "vecSize.size() = " << vecSize.size() << ", vecSize.capacity() = " << vecSize.capacity() << endl;
    
    //reserve
    vecServe.reserve(3);
    cout << "vecServe.size() = " << vecServe.size() << ", vecServe.capacity() = " << vecServe.capacity() << endl;

    return 0;
}

输出结果:

4 vector中的size和capacity的区别

(1)size

        指的是当前 vector 中实际存储的元素数量

(2)capacity

        指的是当前 vector 在重新分配内存之前可以存储的元素数量

#include <iostream>
#include <vector>

int main() {
    std::vector<int> v;

    std::cout << "Initial size: " << v.size() << std::endl;
    std::cout << "Initial capacity: " << v.capacity() << std::endl;

    for (int i = 0; i < 10; i++) {
        v.push_back(i);
        std::cout << "Size: " << v.size() << ", Capacity: " << v.capacity() << std::endl;
    }

    return 0;
}

输出:

Initial size: 0
Initial capacity: 0
Size: 1, Capacity: 1
Size: 2, Capacity: 2
Size: 3, Capacity: 4
Size: 4, Capacity: 4
Size: 5, Capacity: 8
Size: 6, Capacity: 8
Size: 7, Capacity: 8
Size: 8, Capacity: 8
Size: 9, Capacity: 16
Size: 10, Capacity: 16

        在上面的例子中,我们创建了一个空的vector,并输出了其初始大小和容量。然后,我们向vector中添加了10个元素,并在每次添加后输出了其大小和容量。可以看到,在vector的元素数量增加时,其容量会随之增加,但其容量的增加方式可能会有所不同,因为vector重新分配内存时是按照一定的规则来扩大容量的。

5 vector的元素类型可以时引用吗?

不可以。

        在 C++ 中,引用是一种别名,它必须在定义时被初始化,并且一旦初始化后就不能再被改变。而且,引用类型的大小是指向其引用对象的大小,它本身并不拥有存储空间。这与 vector 的要求不同,因为 vector 要求元素类型必须是可复制的,即它可以拷贝自己,而引用类型是不可拷贝的。因此,如果 vector 的元素类型是引用类型,就无法满足可复制的要求。

        另外,引用类型也不是可销毁的,因为他并没有自己的存储空间,所以无法被销毁。而 vector 要求元素类型必须是可销毁的,即可以被析构函数销毁。因此,如果 vector 的元素类型是引用类型,就无法满足可销毁的要求。

        综上所述,vector 的元素类型不能是引用类型。

6 vector迭代器失效的情况

(1)迭代器

        在 C++ 中,迭代器是一种对象,它允许我们在容器(如vector、list、set等)中遍历元素,类似于指针。使用迭代器,我们可以访问容器中的元素,或者对容器中的元素进行修改、删除、添加等操作。

        在使用 vector 容器时,我们通常会使用迭代器来访问其中的元素,例如:

std::vector<int> vec = {1, 2, 3, 4};
for (auto it = vec.begin(); it != vec.end(); ++it) {
    std::cout << *it << " ";
}

        迭代器本质是一个对象,它包含了指向容器中某个元素的指针,以及一些方法(如++、--、*等)来访问容器中的元素。迭代器可以用于访问容器中的所有元素,包含在容器中的开始、结束位置之间的元素,因此它们非常有用。

        迭代器也有很多种类型,如正向迭代器、反向迭代器、常量迭代器等,它们都提供了不同的功能和使用场景。通过使用迭代器,我们可以在不了解容器内部实现细节的情况下,轻松地遍历容器中的所有元素,并进行各种操作,这使得 C++ 中的容器非常强大和灵活。

(2)迭代器失效的情况

        在 C++ 标准库中,vector 是一种动态数组,可以自动调整大小以适应不同的数据量。因此,向 vector 中添加或删除元素时,可能会导致它内部的存储器重新分配,旧的内存区域被释放,新的内存区域被分配,所有指向就内存区域的指针和迭代器都将失效。以下是迭代器失效的情况:

  • 在向vector中添加元素时,如果当前的存储空间不足以容纳新元素,vector 将自动分配更多的内存空间。在这种情况下,原有的迭代器会失效,因为新的内存区域可能与旧的内存区域地址不同,指向旧内存区域的迭代器就无法访问新内存区域中的元素了。
  • 在从 vector 中删除元素时,删除一个元素可能会导致后续元素的地址发生变化,因此指向被删除元素之后的所有元素的迭代器都会失效。
  • 在从 vector 中插入元素时,插入一个元素可能会导致后续元素的地址发生变化,因此指向插入位置之后的所有元素的迭代器都会失效。
  • 在调用 vector 的 resize() 方法时,如果当前的大小比新的大小要小,vector 将会分配更多的内存。在这种情况下,原有的迭代器会失效,因为新的内存区域可能与旧的内存区域地址不同。
  • 在调用 vector 的 reserve() 方法时,如果当前分配的存储空间不足以容纳新元素,vector 将分配更多的内存。在这种情况下,原因的迭代器会失效,因为新的内存区域可能与旧的内存区域地址不同。
  • 在使用排序算法对 vector 中的元素进行排序时,所有指向元素的迭代器都会失效。因为排序算法可能会重新排列元素,导致元素在 vector 中的位置发生变化。

(3)避免迭代器失效的方法

        1)尽量使用迭代器之前,检查它是否仍然有效,避免使用无效的迭代器

        2)避免手动分配内存,而是使用 vector 提供的方法来添加或删除元素

        3)在使用迭代器访问 vector 中的元素时,不要修改 vector 中的大小,以避免迭代器失效

        4)当 vector 容器的大小可能会被改变时,使用 const_iterator 而不是 iterator,因为 const_iterator 保证不会修改 vector 的大小

        5)如果必须在 vector 中间插入或删除元素,可以使用 insert() 和 erase() 方法来避免迭代器失效

        6)在对 vector 进行排序之前,可以使用另一个 vector 对元素进行排序,然后使用 swap() 方法将排序后的 vector 与原 vector 交换,以避免迭代器失效

        总之,在使用 vector 迭代器时,需要时刻注意可能导致迭代器失效的操作,并采取相应的措施来避免迭代器失效。如果不小心导致了迭代器失效,就需要重新获取一个新的迭代器来访问 vector 中的元素。

  • 1
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值