C++语法知识点合集:8.vector


一、 vector的使用

1.vector的建造

创建 std::vector 的不同方式

// constructing vectors
#include <iostream>
#include <vector>

int main()
{
    // constructors used in the same order as described above:
    // 空的 int 类型向量
    std::vector<int> first;
    // 创建了一个名为 second 的向量,包含4个元素,每个元素的值都是100。
    std::vector<int> second(4, 100);
    // 迭代器范围构造函数
    // 创建了一个名为 third 的向量,它通过遍历 second 的元素构造而成,因此 third 的内容与 second 相同。
    std::vector<int> third(second.begin(), second.end());
    // 拷贝构造函数
    // 创建了一个名为 fourth 的向量,它是 third 向量的副本,因此 fourth 的内容与 third 相同。
    std::vector<int> fourth(third);

    // 通过数组的首地址 myints 和末地址 myints + sizeof(myints) / sizeof(int) 构造了一个名为 fifth 的向量。
    int myints[] = {16, 2, 77, 29};
    std::vector<int> fifth(myints, myints + sizeof(myints) / sizeof(int));

    std::cout << "The contents of fifth are:";
    for (std::vector<int>::iterator it = fifth.begin(); it != fifth.end(); ++it)
        std::cout << ' ' << *it; //*it 解引用迭代器,获取当前元素的值
    // endl 是一个操纵符,它的作用是向输出流插入一个换行符,并刷新缓冲区。
    std::cout << '\n';

    return 0;
}

2.vector iterator 的使用

在这里插入图片描述

// vector::begin/end
#include <iostream>
#include <vector>

int main ()
{
  std::vector<int> myvector;
  for (int i=1; i<=5; i++) myvector.push_back(i);

  std::cout << "myvector contains:";
  for (std::vector<int>::iterator it = myvector.begin() ; it != myvector.end(); ++it)
    std::cout << ' ' << *it;
  std::cout << '\n';

  return 0;
}
#include <iostream>
#include <vector>

int main()
{
    std::vector<int> myvector(5); // 创建一个包含5个默认值的int类型向量,每个元素初始值为0

    int i = 0; // 定义并初始化一个整型变量i为0

    std::vector<int>::reverse_iterator rit = myvector.rbegin();
    // 定义一个反向迭代器rit,初始化为指向myvector的反向起始位置(即最后一个元素)

    for (; rit != myvector.rend(); ++rit) // 使用反向迭代器遍历myvector,从最后一个元素到第一个元素
        *rit = ++i;                       // 将当前迭代器指向的元素赋值为i递增后的值(即1到5)

    std::cout << "myvector contains:";

    for (std::vector<int>::iterator it = myvector.begin(); it != myvector.end(); ++it)
        // 使用正向迭代器遍历myvector,从第一个元素到最后一个元素
        std::cout << ' ' << *it; // 输出当前元素的值,前面加一个空格

    std::cout << '\n';

    return 0;
}

3.vector 空间增长问题

  1. size 获取数据个数
#include <iostream>
#include <vector>

int main()
{
    std::vector<int> myints; // 创建一个空的int类型向量

    std::cout << "0. size: " << myints.size() << '\n';
    // 输出当前向量的大小,应该是0,因为向量刚刚创建,还没有添加任何元素

    for (int i = 0; i < 10; i++)
        myints.push_back(i);
    // 使用for循环向向量中添加10个元素,这些元素的值分别为0到9

    std::cout << "1. size: " << myints.size() << '\n';
    myints.insert(myints.end(), 10, 100);
    // 在向量的末尾插入10个值为100的元素

    std::cout << "2. size: " << myints.size() << '\n';
    // 输出当前向量的大小,应该是20,因为刚刚插入了10个新元素

    myints.pop_back();
    // 删除向量的最后一个元素

    std::cout << "3. size: " << myints.size() << '\n';
    // 输出当前向量的大小,应该是19,因为刚刚删除了1个元素

    return 0; // 返回0,表示程序成功结束
}

  1. capacity
#include <iostream>
#include <vector>

int main()
{
    std::vector<int> myvector; // 创建一个空的int类型向量

    // 在向量中设置一些内容:
    for (int i = 0; i < 100; i++)
        myvector.push_back(i);
    // 使用for循环向向量中添加100个元素,这些元素的值为0到99

    std::cout << "size: " << (int)myvector.size() << '\n';
    // 输出当前向量的大小,表示向量中元素的个数,应该是100

    std::cout << "capacity: " << (int)myvector.capacity() << '\n';
    // 输出当前向量的容量,表示向量在不重新分配内存的情况下可以容纳的元素数量

    std::cout << "max_size: " << (int)myvector.max_size() << '\n';
    // 输出当前向量的最大可能大小,表示向量在理论上能够容纳的最大元素数量

    return 0; // 返回0,表示程序成功结束
}

capacity的代码在vs和g++下分别运行会发现,vs下capacity是按1.5倍增长的,g++是按2倍增长的。

  1. empty
#include <iostream>
#include <vector>
int main()
{
    std::vector<int> myvector;

    int sum(0);
    for (int i = 1; i <= 10; i++)
        myvector.push_back(i);
    while (!myvector.empty())
    // 使用while循环,持续执行直到向量为空
    {
        sum += myvector.back();
        // 将向量末尾的元素值加到sum中

        myvector.pop_back();
        // 删除向量末尾的元素
    }

    std::cout << "total: " << sum << '\n';
    return 0;
}

  1. resize
#include <iostream>
#include <vector>

int main()
{
    std::vector<int> myvector;
    for (int i = 1; i < 10; i++)
        myvector.push_back(i);
    // 使用for循环向向量中添加1到9的整数,这些元素依次添加到向量的末尾

    myvector.resize(5);
    // 将向量的大小调整为5,超过5的元素将被删除。保留前5个元素:1, 2, 3, 4, 5

    myvector.resize(8, 100);
    // 将向量的大小调整为8,新增加的元素初始化为100。因此,向量现在包含:1, 2, 3, 4, 5, 100, 100, 100

    myvector.resize(12);
    // 将向量的大小调整为12,新增的元素用默认值0进行填充。向量现在包含:1, 2, 3, 4, 5, 100, 100, 100, 0, 0, 0, 0

    std::cout << "myvector contains:";

    for (int i = 0; i < myvector.size(); i++)
        // 使用for循环遍历向量的所有元素
        std::cout << ' ' << myvector[i];
    std::cout << '\n';
    return 0;
}

resize在开空间的同时还会进行初始化,影响size。

  1. reserve

reserve只负责开辟空间,如果确定知道需要用多少空间,reserve可以缓解vector增容的代价缺陷问题。

#include <iostream>
#include <vector>

int main()
{
    std::vector<int>::size_type sz; // 定义一个变量sz,用于存储向量的容量大小
    std::vector<int> foo;           // 创建一个空的int类型向量foo
    sz = foo.capacity();            // 获取foo当前的容量并存储到sz中
    std::cout << "making foo grow:\n";

    for (int i = 0; i < 100; ++i)
    {
        foo.push_back(i); // 将整数i依次添加到foo中
        if (sz != foo.capacity())
        {
            sz = foo.capacity(); // 如果foo的容量发生变化,则更新sz
            std::cout << "capacity changed: " << sz << '\n';
        }
    }

    std::vector<int> bar; // 创建一个空的int类型向量bar
    sz = bar.capacity();
    bar.reserve(100); // 预先将bar的容量设置为100
    std::cout << "making bar grow:\n";

    for (int i = 0; i < 100; ++i)
    {
        bar.push_back(i); // 将整数i依次添加到bar中
        if (sz != bar.capacity())
        {
            sz = bar.capacity(); // 如果bar的容量发生变化,则更新sz
            std::cout << "capacity changed: " << sz << '\n';
        }
    }
    return 0;
}

output:

making foo grow:
capacity changed: 1
capacity changed: 2
capacity changed: 4
capacity changed: 8
capacity changed: 16
capacity changed: 32
capacity changed: 64
capacity changed: 128
making bar grow:
capacity changed: 100
原因:
在这里插入图片描述
在这里插入图片描述

4.vector 增删查改

  1. push_back
#include <iostream>
#include <vector>

int main()
{
    std::vector<int> myvector; // 定义一个整数类型的 vector 容器 myvector,用于存储整数
    int myint;

    std::cout << "Please enter some integers (enter 0 to end):\n";
    do
    {
        std::cin >> myint;         // 从标准输入读取一个整数并存储在 myint 中
        myvector.push_back(myint); // 将读取的整数添加到 myvector 容器的末尾
    } while (myint);
    // 循环直到用户输入 0,0 作为结束标志,不再添加到 myvector 中

    std::cout << "myvector stores " << int(myvector.size()) << " numbers.\n";

    return 0;
}

  1. pop_back
#include <iostream>
#include <vector>

int main()
{
    std::vector<int> myvector; // 定义一个存储整数的vector对象myvector
    int sum(0);

    myvector.push_back(100); // 向myvector的末尾添加整数100
    myvector.push_back(200);
    myvector.push_back(300);

    while (!myvector.empty()) // 当myvector不为空时,进入循环
    {
        sum += myvector.back(); // 取出myvector末尾的元素并加到sum中
        myvector.pop_back();    // 删除myvector末尾的元素
    }

    std::cout << "The elements of myvector add up to " << sum << '\n'; // 输出myvector中所有元素的和

    return 0;
}

  1. find

查找。(注意这个是算法模块实现,不是vector的成员接口)
返回值指向范围中比较等于 val 的第一个元素的迭代器。如果没有元素匹配,函数返回最后一个

#include <iostream>
#include <algorithm> // std::find
#include <vector>

int main()
{
    int myints[] = {10, 20, 30, 40}; // 定义一个整数数组 myints,包含 4 个元素
    int *p;                          // 定义一个指针 p,用于存储找到的元素的地址
    // 在数组 myints 中查找值为 30 的元素,返回指向该元素的指针
    p = std::find(myints, myints + 4, 30);
    // 如果找到的指针 p 不是数组末尾的地址,则说明找到了元素
    if (p != myints + 4)
        std::cout << "Element found in myints: " << *p << '\n'; // 输出找到的元素值
    else
        std::cout << "Element not found in myints\n";
    // 用 myints 数组的元素初始化一个 vector 对象 myvector
    std::vector<int> myvector(myints, myints + 4);
    std::vector<int>::iterator it;

    it = find(myvector.begin(), myvector.end(), 30);
    // 如果找到的迭代器 it 不是 vector 的末尾,则说明找到了元素
    if (it != myvector.end())
        std::cout << "Element found in myvector: " << *it << '\n';
    else
        std::cout << "Element not found in myvector\n";

    return 0;
}

  1. insert 在position之前插入val
#include <iostream>
#include <vector>

int main()
{
    std::vector<int> myvector(3, 100); // 定义一个包含3个元素的vector对象myvector,每个元素值为100
    std::vector<int>::iterator it;
    it = myvector.begin();
    it = myvector.insert(it, 200); // 在myvector的第一个位置插入值为200的元素,it更新为指向新插入的元素

    myvector.insert(it, 2, 300); // 在it位置之前插入两个值为300的元素

    // "it"指向的元素位置已经无效,重新获取一个指向vector开头的迭代器
    it = myvector.begin();
    // 定义一个包含2个元素的vector对象anothervector,每个元素值为400
    std::vector<int> anothervector(2, 400);
    myvector.insert(it + 2, anothervector.begin(), anothervector.end()); // 在myvector中第3个位置插入anothervector的所有元素

    int myarray[] = {501, 502, 503};                         // 定义一个包含3个元素的数组myarray
    myvector.insert(myvector.begin(), myarray, myarray + 3); // 将myarray的所有元素插入到myvector的开头

    std::cout << "myvector contains:";
    for (it = myvector.begin(); it < myvector.end(); it++)
        std::cout << ' ' << *it;
    std::cout << '\n';

    return 0;
}

  1. erase 删除position位置的数据

指向函数调用删除的最后一个元素之后的元素的新位置的迭代器。如果操作删除了序列中的最后一个元素,则指向容器末端

#include <iostream>
#include <vector>

int main()
{
    std::vector<int> myvector; // 定义一个空的整数vector对象myvector

    // 设置一些值(从1到10)
    for (int i = 1; i <= 10; i++)
        myvector.push_back(i);

    // 删除第6个元素
    myvector.erase(myvector.begin() + 5); // 删除myvector中的第6个元素(索引为5)

    // 删除前3个元素
    myvector.erase(myvector.begin(), myvector.begin() + 3); // 删除myvector中的前3个元素

    std::cout << "myvector contains:";
    for (unsigned i = 0; i < myvector.size(); ++i)
        std::cout << ' ' << myvector[i];
    std::cout << '\n';

    return 0;
}
  1. swap
// swap vectors
#include <iostream>
#include <vector>

int main()
{
    std::vector<int> foo(3, 100); // three ints with a value of 100
    std::vector<int> bar(5, 200); // five ints with a value of 200

    foo.swap(bar);

    std::cout << "foo contains:";
    for (unsigned i = 0; i < foo.size(); i++)
        std::cout << ' ' << foo[i];
    std::cout << '\n';

    std::cout << "bar contains:";
    for (unsigned i = 0; i < bar.size(); i++)
        std::cout << ' ' << bar[i];
    std::cout << '\n';

    return 0;
}
  1. operator[]
#include <iostream>
#include <vector>

int main()
{
    std::vector<int> myvector(10); // 定义一个包含10个元素的vector对象myvector,所有元素都初始化为0

    std::vector<int>::size_type sz = myvector.size();

    // 赋值一些数值:
    for (unsigned i = 0; i < sz; i++)
        myvector[i] = i; // 使用下标运算符[]给myvector中的每个元素赋值,使其值等于其索引

    // 使用下标运算符[]反转vector:
    for (unsigned i = 0; i < sz / 2; i++) // 只需遍历前一半元素
    {
        int temp;                           // 定义一个临时变量temp
        temp = myvector[sz - 1 - i];        // 将myvector末尾的元素赋值给temp
        myvector[sz - 1 - i] = myvector[i]; // 将myvector前半部分的元素值赋给对应的末尾位置
        myvector[i] = temp;                 // 将temp中的值(原末尾元素)赋给myvector的前半部分
    }

    std::cout << "myvector contains:";
    for (unsigned i = 0; i < sz; i++)
        std::cout << ' ' << myvector[i];
    std::cout << '\n';

    return 0;
}

5.vector 迭代器失效问题*

迭代器的主要作用就是让算法能够不用关心底层数据结构,其底层实际就是一个指针,或者是对
指针进行了封装,比如:vector的迭代器就是原生态指针T* 。因此迭代器失效,实际就是迭代器
底层对应指针所指向的空间被销毁了,而使用一块已经被释放的空间,造成的后果是程序崩溃(即
如果继续使用已经失效的迭代器,程序可能会崩溃)。

  1. 插入元素导致迭代器失效:
    当向 vector 中插入元素时,特别是当插入的位置不是 vector 的末尾时,vector 可能需要重新分配内存。这是因为 vector 是基于数组实现的,如果插入操作导致 vector 的容量不足,vector 会分配一块新的更大的内存区域,并将现有元素复制到新的位置。

迭代器失效的原因:

  • 如果 vector 重新分配了内存,那么所有指向旧内存的迭代器都会失效。
  • 如果在中间插入元素,插入位置后的所有迭代器都会失效,因为这些元素的位置可能会发生改变。

避免方法:

  • 插入元素后重新获取迭代器。
  • 避免在遍历 vector 时进行插入操作。
  1. 删除元素导致迭代器失效
    当从 vector 中删除元素时,vector 中后续的元素会向前移动以填补被删除元素留下的空位。这会导致某些迭代器失效。

迭代器失效的原因:

  • 如果删除的元素不是最后一个元素,则删除位置之后的所有迭代器都会失效,因为这些元素的位置改变了。

避免方法:

  • 在删除操作后重新获取迭代器。
  • 使用返回的有效迭代器(erase 操作返回的是指向被删除元素的下一个元素的迭代器)。
  1. 容量变化导致的迭代器失效
    vector 的容量是动态增长的,每次插入新元素时,如果当前容量不足以容纳新的元素,vector 会重新分配一块更大的内存并复制现有元素。

迭代器失效的原因:

  • 每次重新分配内存后,所有迭代器都会失效,因为它们仍指向旧的内存位置。
    避免方法:
  • 可以通过调用 reserve() 方法提前分配足够的空间,以避免不必要的内存重新分配。
  1. 举例
#include <iostream>
using namespace std;
#include <vector>
int main()
{
    vector<int> v{1, 2, 3, 4, 5, 6};
    auto it = v.begin();
    v.assign(100, 8);
    while (it != v.end())
    {
        cout << *it << " ";
        ++it;
    }
    cout << endl;
    return 0;
}

在执行 v.assign(100, 8); 之后,v 中的元素全部变为100个值为8的元素,并且这些元素会被存储在一个新的内存区域。因此,原来的迭代器 it 不再指向有效的内存位置。接下来在 while 循环中使用这个失效的迭代器会导致未定义行为。

修改代码

#include <iostream>
using namespace std;
#include <vector>
int main()
{
    vector<int> v{1, 2, 3, 4, 5, 6};

    v.assign(100, 8);
    auto it = v.begin();
    while (it != v.end())
    {
        cout << *it << " ";
        ++it;
    }
    cout << endl;
    return 0;
}
  1. 举例2
#include <iostream>
using namespace std;
#include <vector>
int main()
{
    int a[] = {1, 2, 3, 4};
    vector<int> v(a, a + sizeof(a) / sizeof(int));
    // 使用find查找3所在位置的iterator
    vector<int>::iterator pos = find(v.begin(), v.end(), 3);
    // 删除pos位置的数据,导致pos迭代器失效。
    v.erase(pos);
    cout << *pos << endl; // 此处会导致非法访问
    return 0;
}

在 C++ 中,erase删除pos位置元素后,pos位置之后的元素会往前搬移,没有导致底层空间的改变,理
论上讲迭代器不应该会失效,但是:如果pos刚好是最后一个元素,删完之后pos刚好是end的位置,而end位置是没有元素的,那么pos就失效了。因此删除vector中任意位置上元素时,vs就认为该位置迭代器失效了。

修改:

#include <iostream>
using namespace std;
#include <vector>
int main()
{
    int a[] = {1, 2, 3, 4};
    vector<int> v(a, a + sizeof(a) / sizeof(int));
    // 使用find查找3所在位置的iterator
    vector<int>::iterator pos = find(v.begin(), v.end(), 3);
    // 删除pos位置的数据,并返回指向下一个有效元素的迭代器
    if (pos != v.end())
    {
        pos = v.erase(pos);
    }
    // 在检查pos是否为有效的结束位置后访问元素
    if (pos != v.end())
    {
        cout << *pos << endl; // 输出删除后的位置的新元素
    }
    else
    {
        cout << "No more elements in the vector." << endl; // 如果已到达end,则提示
    }
    cout << *pos << endl; // 此处会导致非法访问
    return 0;
}
  1. 举例3
#include <iostream>
using namespace std;
#include <vector>
int main()
{
    vector<int> v{1, 2, 3, 4};
    auto it = v.begin();
    while (it != v.end())
    {
        if (*it % 2 == 0)
            v.erase(it);
        ++it;
    }
    return 0;
}

迭代器失效:在 std::vector 中,当你调用 erase 删除元素时,所有指向被删除元素以及其后元素的迭代器都会失效。换句话说,删除操作会导致当前迭代器 it 失效。失效的迭代器如果继续使用,可能会导致未定义行为,程序可能崩溃或输出错误的结果。

修改:

#include <iostream>
using namespace std;
#include <vector>
int main()
{
    vector<int> v{1, 2, 3, 4};
    auto it = v.begin();
    while (it != v.end())
    {
        if (*it % 2 == 0)
            it = v.erase(it);
        else
            ++it;
    }
    return 0;
}

在 erase 操作之后,erase 方法返回一个新的有效迭代器,该迭代器指向被删除元素之后的元素。通过将 it 赋值为这个新迭代器,可以确保后续的操作是基于有效的迭代器进行的。

二、vector深度剖析及模拟实现

1.使用memcpy拷贝问题

如果对象中涉及到资源管理时,千万不能使用memcpy进行对象之间的拷贝,因为
memcpy是浅拷贝,否则可能会引起内存泄漏甚至程序崩溃。

2.二维动态数组理解

在这里插入图片描述

vector 是动态数组
而 vector<vector< int > > 就是一个存储 vector< int > 的 vector,也就是说它是一个二维的动态数组结构

#include <string.h>
#include <iostream>
#include <algorithm>
#include <vector>
using namespace std;
class Solution
{
public:
    vector<vector<int>> generate(int numRows) // 定义一个成员函数 generate,返回值类型是二维整数向量,参数是整数 numRows
    {
        vector<vector<int>> vv(numRows);  // 声明一个二维向量 vv,大小为 numRows,用于存储帕斯卡三角形的行
        for (int i = 0; i < numRows; ++i) // 遍历每一行
        {
            vv[i].resize(i + 1, 1); // 将当前行的大小设为 i+1,并初始化所有元素为1
        }
        for (int i = 2; i < numRows; ++i) // 从第三行开始遍历每一行
        {
            for (int j = 1; j < i; ++j) // 遍历当前行的每个元素(除了第一个和最后一个)
            {
                vv[i][j] = vv[i - 1][j] + vv[i - 1][j - 1]; // 设置当前元素为上一行左边和上边元素的和
            }
        }
        return vv; // 返回构造好的帕斯卡三角形
    }
};


  • 8
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值