什么是容器

在这里插入图片描述

  • 通过cppreference的介绍,容器是一种通用的类模板和算法的集合,可以允许程序员快速的实现数据结构。c++11之前只有两种容器(序列和关联),c++11后多了无序关联容器,今天我们介绍序列式容器—— vector

基本定义

在这里插入图片描述

  • 我们要讲的是第一个,他接受两个模板参数,第一个是vector存储的数据类型,另一个是分配器,vector的底层是动态数组,用来分配空间。简单介绍一下另一个 vector, 它的第二个参数是std::pmr::polymorphic_allocato,它是C++17 引入的一个运行时可切换的内存分配器,你可以在运行时决定:用普通堆、内存池、栈上 buffer、共享内存分配内存

成员类型

  • 这里是对模板参数进行了封装,提供了一些比较明确的类型
    在这里插入图片描述

构造函数

  • 这里包括了类的默认构造、拷贝构造、有参构造等函数
    在这里插入图片描述
  • 使用示例
#include <vector>

int main() {
    // 1. 默认构造函数
    std::vector<int> v1;                     // 空 vector

    // 2. 填充构造函数:包含 10 个元素,每个元素值为 42
    std::vector<int> v2(10, 42);

    // 3. 范围构造函数:使用数组迭代器构造
    int arr[] = {1, 2, 3, 4, 5};
    std::vector<int> v3(std::begin(arr), std::end(arr));

    // 4. 拷贝构造函数:复制 v3 的内容
    std::vector<int> v4(v3);

    // 5. 移动构造函数:将 v4 的资源转移给 v5
    std::vector<int> v5(std::move(v4));

    // 6. initializer_list 构造函数:使用花括号列表初始化
    std::vector<int> v6{10, 20, 30, 40};

    // 7. 分配器构造函数(较少单独使用,此处仅作示意)
    // std::allocator<int> alloc;
    // std::vector<int> v7(alloc);

    return 0;
}

成员函数

元素访问

  • at 用来访问指定index的元素,访问前会进行越界检查,越界会抛异常;operator[]可以使用[]访问元素;front可以直接访问容器首个元素;back访问容器最后一个有效的元素;data用来获取vector底层动态数组的原始指针
    在这里插入图片描述
  • 使用示例
#include <iostream>
#include <vector>

int main()
{
    std::vector<int> v{10, 20, 30, 40, 50};

    // 1. operator[]
    std::cout << "v[2] = " << v[2] << "\n"; // 不检查越界

    // 2. at(带边界检查)
    try {
        std::cout << "v.at(3) = " << v.at(3) << "\n";
        // std::cout << v.at(10); // 会抛异常
    } catch (const std::out_of_range& e) {
        std::cout << "out_of_range: " << e.what() << "\n";
    }

    // 3. front(第一个元素)
    std::cout << "front = " << v.front() << "\n";

    // 4. back(最后一个元素)
    std::cout << "back = " << v.back() << "\n";

    // 5. data(底层连续内存)
    int* ptr = v.data();
    std::cout << "data[0] = " << *ptr << "\n";

    // 用 data() 遍历
    std::cout << "Using data(): ";
    for (size_t i = 0; i < v.size(); ++i)
    {
        std::cout << *(ptr + i) << " ";
    }
    std::cout << "\n";

    return 0;
}

迭代器

  • 迭代器可以用来访问元素,cpp11引入了范围for,这个特性需要支持类支持迭代器
    在这里插入图片描述
  • 使用示例
vector<int> vec{1,2,3,4,5}
for (auto v:vec)
{
	std::cout << v << " ";
}
for (auto it = vec.begin(); it != vec.end(); ++it)
	std::cout << v << " ";
  • 范围for会被转换成下面这样
  std::vector<int, std::allocator<int> > vec = std::vector<int, std::allocator<int> >{std::initializer_list<int>{1, 2, 3, 4, 5}, std::allocator<int>()};
  {
    std::vector<int, std::allocator<int> > & __range1 = vec;
    __gnu_cxx::__normal_iterator<int *, std::vector<int, std::allocator<int> > > __begin1 = __range1.begin();
    __gnu_cxx::__normal_iterator<int *, std::vector<int, std::allocator<int> > > __end1 = __range1.end();
    for(; __gnu_cxx::operator!=(__begin1, __end1); __begin1.operator++()) {
      int v = __begin1.operator*();
      std::operator<<(std::cout.operator<<(v), " ");
    }
  }

容量

  • empty 函数判断容器是否有有效的元素;size返回容器中有效元素的个数;max_size返回当前系统可以容纳的最大元素数量;reserve用来预分配容器容量;shrink_to_fit用来对容器进行缩容;具体行为依赖编译器实现;clear函数不会改变capacity
    在这里插入图片描述
  • 使用示例
#include <iostream>
#include <vector>

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

    // 1. empty
    std::cout << "empty: " << std::boolalpha << v.empty() << "\n";

    // 2. reserve(预分配空间)
    v.reserve(10);
    std::cout << "after reserve(10): capacity = " << v.capacity() << "\n";

    // 插入元素
    for (int i = 1; i <= 5; ++i)
    {
        v.push_back(i);
    }

    // 3. size
    std::cout << "size: " << v.size() << "\n";

    // 4. capacity
    std::cout << "capacity: " << v.capacity() << "\n";

    // 5. max_size
    std::cout << "max_size: " << v.max_size() << "\n";

    // 6. shrink_to_fit(请求释放多余空间)
    v.shrink_to_fit();
    std::cout << "after shrink_to_fit:\n";
    std::cout << "size: " << v.size() << "\n";
    std::cout << "capacity: " << v.capacity() << "\n";

    // 再清空元素
    v.clear();
    std::cout << "after clear:\n";
    std::cout << "empty: " << v.empty() << "\n";
    std::cout << "size: " << v.size() << "\n";
    std::cout << "capacity (still there): " << v.capacity() << "\n";

    return 0;
}

修改操作

  • clear函数只会清空元素,不会改变数量;insert 可以在指定位置插入一个或一段区间的元素,返回插入的第一个元素的迭代器;emplace可以原地构造一个元素并插入指定位置;push_back在容器末尾插入一个元素;emplace_back原地构造一个元素并插入末尾;当传入临时对象时,emplace_back会比push_back高效,如果是一个已经构造好的元素,并不能确定谁更高效;pop_back移除末尾的元素;resize可以重新指定容器的大小,多余的元素以对应类型的默认值填充;swap可以用来交换两个容器的内容
  • emplace_back更适合传需要构造的参数,如果是一个构造好的对象,优先使用push_back
    在这里插入图片描述
  • 示例
#include <iostream>
#include <vector>

void print(const std::vector<int>& v)
{
    for (auto x : v) std::cout << x << " ";
    std::cout << "\n";
}

int main()
{
    std::vector<int> v{1, 2, 3};

    // 1. push_back
    v.push_back(4);  // [1 2 3 4]

    // 2. emplace_back(就地构造)
    v.emplace_back(5); // [1 2 3 4 5]

    print(v);

    // 3. insert(在指定位置插入)
    v.insert(v.begin() + 1, 99); // [1 99 2 3 4 5]
    print(v);

    // 4. insert range(用传统方式模拟)
    std::vector<int> other{7, 8, 9};
    v.insert(v.end(), other.begin(), other.end()); // [1 99 2 3 4 5 7 8 9]
    print(v);

    // 5. emplace(在中间构造)
    v.emplace(v.begin() + 2, 88); // [1 99 88 2 3 4 5 7 8 9]
    print(v);

    // 6. erase(删除元素)
    v.erase(v.begin() + 1); // 删除 99
    print(v);

    // 删除区间
    v.erase(v.begin() + 2, v.begin() + 4);
    print(v);

    // 7. pop_back
    v.pop_back();
    print(v);

    // 8. resize
    v.resize(5); // 截断或补默认值
    print(v);

    v.resize(8, 100); // 不够就补 100
    print(v);

    // 9. swap
    std::vector<int> v2{1000, 2000};
    v.swap(v2);

    std::cout << "v: ";
    print(v);

    std::cout << "v2: ";
    print(v2);

    // 10. clear
    v.clear();
    std::cout << "v after clear: ";
    print(v);

    return 0;
}

这篇文章就写到这里了,如果觉得写的还不错,欢迎点赞关注,如果有错误还请批评指正

Logo

汇聚全球AI编程工具,助力开发者即刻编程。

更多推荐