vector

发布于:2024-12-18 ⋅ 阅读:(79) ⋅ 点赞:(0)

1.vector的介绍 

在C++中,vector是标准模板库(STL)中的一个动态数组类,属于<vector>头文件,如果放在c语言当中,就是我们所熟知的数组,不过,c++将其封装成了一个类,不仅能存放char类型、int类型、浮点型、甚至自定义类型等,vector也包含了许多相关的接口,因此,vector要比c语言的数组复杂许多

2.vector的使用 

 学习像vector等标准模板库的内容时,强烈建议看c++文档,vector在实际中非常的重要,在实际中熟悉常见的接口有以下部分

2.1vector的定义

(costructor)构造函数声明 接口说明
vector()(重点) 无参构造
vector(size_type n, const value_type& val = value_type()) 构造并初始化n个val
vector (const vector& x); (重点) 拷贝构造
vector (InputIterator first, InputIterator last); 使用迭代器进行初始化构造

 代码示例:

#include<vector> //使用vector需要包含此头文件
#include<iostream>
using namespace std;  //对于初学者而言建议使用此头文件,减少了空间域的展开代码

int main()
{
    vector<int> arr1(10); //创建了一个int类型的10个元素的数组arr1,未进行初始化
    vector<int> arr2(5, 5); //创建了一个int类型的5个元素的数组arr2,所有元素均初始化为5
    vector<int> arr3(arr2); //使用arr2拷贝构造了arr3
    auto it1 = arr2.begin(); //迭代器
    auto it2 = arr2.end();
    vector<int> arr4(it1, it2); //使用arr2的迭代器进行构造arr4,构造范围是it1到it2
    return 0;
}

2.2 vector iterator 的使用

iterator的使 用 接口说明
begin + end (重点) 获取第一个数据位置的iterator/const_iterator, 获取最后一个数据的下 一个位置的iterator/const_iterator
rbegin + rend 获取最后一个数据位置的reverse_iterator,获取第一个数据前一个位置 的reverse_iterator

begin 和 end 对应的是正向迭代器

rbegin 和 rend 对应的是反向迭代器 

 图解:

 代码示例:

#include<vector>
#include<iostream>
using namespace std;

int main()
{
    vector<int> arr1(10);
    for (int i = 0; i < 10; i++)
    {
        arr1[i] = i + 1;
    }
    vector<int>::iterator it = arr1.begin();
    auto it1 = arr1.begin(); //与上一行意义相同,使用auto减少了代码量

    while (it != arr1.end()) //使用正向迭代器进行遍历
    {
        cout << *it << " ";
        ++it;
    }
    cout << endl;


    auto rit = arr1.rbegin();
    while (rit != arr1.rend()) //使用反向迭代器遍历
    {
        cout << *rit << " ";
        ++rit;
    }
    cout << endl;

    for (auto a : arr1) //使用范围for遍历,实际上底层调用的还是迭代器
    {
        cout << a << " ";
    }
}

2.3 vector 空间增长问题 

相关接口:

容量空间 接口说明
size() 获取数据个数
capacity() 获取容量大小
empty() 判断是否为空
resize()(重点) 改变vector的size(甚至capacity)
reserve()(重点) 改变vector的capacity

代码示例: 

#include<vector>
#include<iostream>
using namespace std;

int main()
{
    vector<int> arr(10, 1);
    cout << arr.size() << endl; //获取arr的有效元素个数
    cout << arr.capacity() << endl; //获取arr的容量
    cout << arr.empty() << endl; //判断arr是否为空

    arr.resize(8); //改变arr的有效个数,给定的值小于有效元素个数,则减小有效元素个数
    for (auto a : arr)
    {
        cout << a << " ";
    }
    cout << endl;

    arr.resize(12, 4);  //给定的值大于有效元素个数,则使用“4”进行填充,必要时会自动扩容
    for (auto a : arr)
    {
        cout << a << " ";
    }
    return 0;
}

需要了解的细节:

  • capacity的代码在vs和g++下分别运行会发现,vs下capacity是按1.5倍增长的,g++是按2 倍增长的
  • reserve只负责开辟空间,如果确定知道需要用多少空间,reserve可以缓解vector增容的代 价缺陷问题
  • resize在开空间的同时还会进行初始化,影响size 

 对于vector的扩容机制,c++并没有明确的规定,因此取决于编译器,如果想测试一下编译器的扩容机制,可以使用下面这段代码

#include<vector>
#include<iostream>
using namespace std;

// 测试vector的默认扩容机制
void TestVectorExpand()
{
    size_t sz;
    vector<int> v;
    sz = v.capacity();
    cout << "making v grow:\n";
    for (int i = 0; i < 100; ++i)
    {
        v.push_back(i);
        if (sz != v.capacity())
        {
            sz = v.capacity();
            cout << "capacity changed: " << sz << '\n';
        }
    }
}

int main()
{
    TestVectorExpand();
    return 0;
}

在VS2022下运行: 

 

可以明显看出,这里是按1.5倍进行扩容的 

2.4 vector 增删查改  

vector增删查改 接口说明
push_back(重点) 尾插
pop_back(重点) 尾删
find 查找(算法模块实现,非vector成员接口)
insert 在position之前插入value
eraser 删除position位置的数据
swap 交换两个vector的数据空间
operator[](重点) 像数组一样访问其元素

代码示例:

#include<vector>
#include<iostream>
using namespace std;

int main()
{
    vector<int> arr(0); //创建一个空数组
    arr.push_back(1);  //尾插数据"1"
    arr.push_back(2);
    arr.push_back(3);
    arr.push_back(4);
    arr.push_back(5);

    for (auto a : arr)
    {
        cout << a << " ";
    }
    cout << endl;

    arr.pop_back();  //尾删数据
    
    for (auto a : arr)
    {
        cout << a << " ";
    }
    //for (int i = 0; i < 4; i++)  //像数组一样访问
    //{
    //    cout << arr[i] << " ";
    //}

    auto it1 = arr.begin();  //迭代器
    it1 = arr.insert(it1, 3); //向it1位置插入数据并修正it1,防止迭代器失效
    for (auto a : arr)
    {
        cout << a << " ";
    }
    cout << endl;
    arr.erase(it1);  //删除it位置的数据,position就是这里的it,是一个迭代器
    for(auto a : arr)
    {
        cout << a << " ";
    }

    it1 = arr.begin();
    auto it2 = arr.end();
    auto it3 = find(it1, it2, 2); //在迭代器 it1 到 it2 的这段区间内查找“2”
    cout << *it3 << endl;

    vector<int> arr1(3, 0);
    swap(arr, arr1);  //交换两个vector空间的数据
    for (auto a : arr)
    {
        cout << a << " ";
    }
    cout << endl;

    for (auto a : arr1)
    {
        cout << a << " ";
    }
    return 0;
}

 2.5 vector 迭代器失效问题(重点)

在上面的2.4中,有一行代码是这样写的:

it1 = arr.insert(it1, 3);

这个接口自己提供了返回值,那为什么要提供返回值?

如果不提供返回值,没有修正it1,此时的it1已经不是指向之前那个元素了,如果此时使用it1,很有可能造成错误,而且这个错误有可能很难发现,(gcc下,不报错,vs严格检查,此时会报错) 

因此,修正迭代器是非常有必要的,这就涉及迭代器失效问题了 

 1.失效原因

会引起其底层空间改变的操作,都有可能是迭代器失效,比如:resize、reserve、insert、 assign、push_back等,为什么是有可能,原因之一是vector的自动扩容我们不知道什么时候会发生,以及上面所说的问题

#include <iostream>
using namespace std;
#include <vector>
int main()
{
    vector<int> v{ 1,2,3,4,5,6 };
    auto it = v.begin();
    // 将有效元素个数增加到100个,多出的位置使用8填充,操作期间底层会扩容
   // v.resize(100, 8);
    // reserve的作用就是改变扩容大小但不改变有效元素个数,操作期间可能会引起底层容量改变
    // v.reserve(100);
    // 插入元素期间,可能会引起扩容,而导致原空间被释放
    // v.insert(v.begin(), 0);
    // v.push_back(8);
    // 给vector重新赋值,可能会引起底层容量改变
        v.assign(100, 8);
    /*
   出错原因:以上操作,都有可能会导致vector扩容,也就是说vector底层原理旧空间被释
   放掉,而在打印时,it还使用的是释放之间的旧空间,在对it迭代器操作时,实际操作的是一块
   已经被释放的空间,而引起代码运行时崩溃。
   解决方式:在以上操作完成之后,如果想要继续通过迭代器操作vector中的元素,只需给
   it重新赋值即可。
   */
    while (it != v.end())
    {
        cout << *it << " ";
        ++it;
    }
    cout << endl;
    return 0;

2.解决方法 

解决方法很简单,就是在使用前对迭代器重新赋值 


网站公告

今日签到

点亮在社区的每一天
去签到