std::vector

发布于:2024-10-10 ⋅ 阅读:(8) ⋅ 点赞:(0)

std::vector是C++标准库中一个非常强大的容器类,它提供了动态数组的功能。std::vector可以自动调整大小,提供了随机访问的能力,同时还支持在序列的尾部高效地添加和删除元素。

当创建一个空的std::vector对象时,它不分配任何内存,大小是0。当开始添加元素时,std::vector会动态分配内存。通常情况下,第一次分配的内存大小由具体的实现决定,但通常是一个小的初始值,比如16或32个元素大小的内存块。

当元素数量超过当前内存容量时,std::vector将重新分配内存,并将元素从旧的内存块复制到新的内存块中。新的内存块的大小通常是当前元素数量的两倍或更多,具体取决于具体的实现和策略。重新分配内存时,可能会有一些额外的内存开销,因为需要考虑到内存对齐和内存管理的一些细节。

std::vector的特性:

  • 动态大小:std::vector的大小可以动态变化,能够根据需要自动扩容
  • 连续存储:像静态数组一样,内部使用连续的内存存储元素,这使得元素访问速度很快
  • 模板类:std::vector是一个模板类,可以存储任何类型的数据

std::vector提供了一系列成员函数来操作和管理动态数组:

  • 构造函数和析构函数

    • std::vector():构造一个空的向量

    • std::vector(size_type n, const T& value = T()):构造一个包含n个具有value值的元素的向量

    • std::vector(const std::vector& other):拷贝构造函数,深拷贝,会复制other中的元素,并存储到新的向量中

    • std::vector(std::vector&& other) noexcept:移动构造函数,将另一个已存在的vector对象的资源移动到新创建的vector中,而不是进行深拷贝

    • ~std::vector():析构函数,释放向量所占用的内存

      std::vector<int> vec;  // 创建vec向量
      
      std::vector<std::string> vec(5, "A");  // 创建一个向量,该向量有5个元素,每个元素的值都是"A"
      
      std::vector<int> vec = {1, 2, 3, 4, 5};  // 创建向量vec,并进行初始化
      std::vector<int> new_vec(vec);  // 创建新的向量new_vec,并深拷贝vec中的元素
      
      std::vector<int> vec = {1, 2, 3, 4, 5};
      std::vector<int> new_vec(std::move(vec));  // 创建新的向量new_vec,并将vec的资源移动到new_vec
      
      在C++标准库中,std::vector的析构函数是隐式生成的,如果用户没有自定义,将会使用编译器默认生成的析构函数。默认情况下,析构函数会按顺序释放元素,然后释放分配的内存。因此,std::vector在其生命周期结束时会自动执行析构函数,确保动态分配的内存被正确释放,从而避免内存泄漏。
      
  • 元素访问

    • operator[]:通过索引访问向量的元素

    • at(value):通过索引访问向量的元素,带边界检查

    • front():访问向量的第一个元素

    • back():访问向量的最后一个元素

    • data():返回一个指向向量第一个元素的指针

      std::vector<int> vec{0, 1, 2, 3, 4, 5};  // 创建向量vec,并进行初始化
      
      std::cout << vec[3] << std::endl;  // 3
      std::cout<< vec.at(3) << std::endl;  // 3
      std::cout<< vec.front() << std::endl;  // 0
      std::cout<< vec.back() << std::endl;  // 5
      int* ptr = vec.data();
      for (size_t i = 0; i < vec.size(); ++i) {
          std::cout << *(ptr + i) << " ";  // 0 1 2 3 4 5
          std::cout << ptr[i] << " ";  // 0 1 2 3 4 5
      }
      
  • 修改器

    • push_back(const T& value):在向量的末尾添加一个元素

    • pop_back():移除向量的最后一个元素

    • emplace_back(Args&&… args):就地构造一个元素,避免额外的复制或移动

    • insert(const_iterator pos, const T& value):在指定位置插入一个元素

    • erase(const_iterator pos):删除指定位置或指定范围内的元素

    • resize():调整向量的大小

    • clear():清除向量中的所有元素

      std::vector<std::string> vec{"A", "B", "C"};
      for (auto value : vec) {
          std::cout << value << std::endl;  // A B C
      }
      
      vec.push_back("D");  // 在向量末尾添加一个元素
      for (auto value : vec) {
          std::cout << value << std::endl;  // A B C D
      }
      
      vec.pop_back();  // 移除向量的最后一个元素
      for (auto value : vec) {
          std::cout << value << std::endl;  // A B C
      }
      
      vec.emplace_back("E");  // 在向量末尾添加一个元素
      for (auto value : vec) {
          std::cout << value << std::endl;  // A B C E
      }
      
      vec.insert(vec.begin() + 1, "F");  // 在索引为1的位置插入元素"F"
      for (auto value : vec) {
          std::cout << value << std::endl;  // A F B C E
      }
      
      std::string values[] = {"X", "Y", "Z"};
      vec.insert(vec.end(), std::begin(values), std::end(values));  // 在向量末尾插入一系列元素
      for (auto value : vec) {
          std::cout << value << std::endl;  // A F B C E X Y Z
      }
      
      vec.erase(vec.begin() + 1);  // 删除索引为1的元素
      for (auto value : vec) {
          std::cout << value << std::endl;  // A B C E X Y Z
      }
      
      vec.erase(vec.begin() + 4, vec.end());  // 删除一系列元素,范围包括索引为4的元素到最后一个元素;范围是左闭右开,vec.end()指向末尾元素的后一位,因此囊括了末尾元素
      for (auto value : vec) {
          std::cout << value << std::endl;  // A B C E
      }
      
      vec.resize(3);  // 调整向量大小,新的向量变小了,会删除多余的元素;A B C
      vec.resize(5);  // 调整向量大小,新的向量变大了,会添加新的元素,默认构造;A B C 空值 空值
      vec.resize(7, "E");  // 调整向量大小,新的向量变大了,会添加指定了值的元素;A B C 空值 空值 E E
      
      vec.clear();  // 清除向量中的所有元素;向量变为空,但分配的内存不会被释放
      std::cout << vec.size() << std::endl;  // 0
      
      /*
      push_back函数用于在向量的末尾添加一个元素,这个元素可以是值,也可以是另一个向量中元素的引用或指针。
      emplace_back函数用于在向量的末尾就地构造一个元素,它可以避免临时对象的创建,而是直接将参数传递给构造函数,从而提高效率。
      emplace_back通常比push_back更高效,因为它避免了复制或移动操作,直接在向量的末尾就地构造元素。
      如果需要构造一个对象并将其添加到向量的末尾,emplace_back是更好的选择;如果需要添加一个已经存在的对象,push_back是合适的。
      */
      
  • 容量

    • empty():判断向量是否为空,返回一个布尔值

    • size():返回向量中元素的数量

    • max_size():返回向量能容纳的最大元素数量

    • capacity():返回向量当前的容量

    • reserve(size_type new_cap):改变向量的容量

    • shrink_to_fit():减小向量的容量以适应其大小

      std::vector<int> vec = {1, 2, 3, 4, 5};
      bool is_empty = vec.empty();  // 如果向量为空,返回1;如果向量不为空,返回0 
      std::cout << is_empty << std::endl;  // 输出0
      
      vec.pop_back();  // 移除末尾元素
      std::cout << vec.size() << std::endl;  // 输出4,向量中还有4个元素
      std::cout << vec.capacity() << std::endl;  // 输出5,虽然移除了一个元素,但占用的内存空间并没有释放,因此容量为5
      std::cout << vec.max_size() << std::endl;  // 4611686018427387903,向量能容纳的最大元素数量
      
      vec.reserve(10);  // 将vec的容量改为10
      std::cout << vec.capacity() << std::endl;  // 输出10
      
      vec.shrink_to_fit();  // 改变容量以适应向量的大小
      std::cout << vec.capacity() << std::endl;  // 输出4,因为向量中有4个元素,因此容量会自动适应向量的大小
      
  • 迭代器

    • begin():返回指向向量第一个元素的迭代器

    • end():返回指向向量尾后一位的迭代器

    • cbegin():返回指向向量第一个元素的const迭代器

    • cend():返回指向向量尾后一位的const迭代器

    • rbegin():返回指向向量最后一个元素的反向迭代器

    • rend():返回指向向量首前一位的反向迭代器

    • crbegin():返回指向向量最后一个元素的const反向迭代器

    • crend(): 返回指向向量首前一位的const反向迭代器

      std::vector<int> vec{1, 2, 3, 4, 5};
      for (auto it = vec.begin(); it != vec.end(); it++) {
          std::cout << *it << std::endl;  // 1 2 3 4 5
      }
      
      for (auto it = vec.cbegin(); it != vec.cend(); it++) {
          std::cout << *it << std::endl;  // 1 2 3 4 5
      }
      
      for (auto it = vec.rbegin(); it != vec.rend(); it++) {
          std::cout << *it << std::endl;  // 5 4 3 2 1 
      }
      
      for (auto it = vec.crbegin(); it != vec.crend(); it++) {
          std::cout << *it << std::endl;  // 5 4 3 2 1 
      }
      
  • 数据操作/赋值操作

    • assign(size_type n, const T& value):替换向量的内容为n个value元素

    • swap(std::vector& other):交换向量的内容

      std::vector<int> vec = {1, 2, 3, 4, 5};
      std::vector<int> new_vec = {6, 7, 8, 9};
      
      vec.assign(new_vec.begin(), new_vec.end());  // 将new_vec的全部元素赋给vec
      for (auto value : vec) {
          std::cout << value << std::endl;  // 6 7 8 9
      }
      
      vec.assign(5, 10);  // 将5个10赋给vec
      for (auto value : vec) {
          std::cout << value << std::endl;  // 10 10 10 10 10
      }
      
      std::vector<int> vec = {1, 2, 3, 4, 5};
      std::vector<int> new_vec = {6, 7, 8, 9};
      
      vec.swap(new_vec);  // 将vec与new_vec的元素互相交换
      for (auto value : vec) {
          std::cout << value << std::endl;  // 6 7 8 9
      }
      
      for (auto value : new_vec) {
          std::cout << value << std::endl;  // 1 2 3 4 5
      }
      
  • 查找操作

    • find(const T& value):查找第一个等于value的元素

    • lower_bound(const T& value):查找第一个不小于value的元素

    • upper_bound(const T& value):查找第一个大于value的元素

      std::vector<int> vec{3, 2, 1, 4, 5};
      
      auto it = std::find(vec.begin(), vec.end(), 3);  // std::find函数用于在向量中查找特定值,并返回指向找到的元素的迭代器,如果未找到,则返回尾后迭代器
      std::cout << *it << std::endl;  // 输出3
      
      auto it = std::find(vec.begin(), vec.end(), 0);  // 要查找的0不在向量中,因此返回尾后迭代器,即指向向量尾后一位的迭代器
      std::cout << *(it - 1) << std::endl;  // 输出5,尾后迭代器前一个元素就是向量的末尾元素
      
      std::vector<int> vec{3, 2, 1, 4, 5};
      
      std::sort(vec.begin(), vec.end());  // 对向量进行排序,默认为升序
      for (auto value : vec) {
          std::cout << value << std::endl;  // 1 2 3 4 5
      }
      
      std::sort(vec.begin(), vec.end(), std::greater<int>());  // 对向量进行降序排序
      for (auto value : vec) {
          std::cout << value << std::endl;  // 5 4 3 2 1
      }
      
      std::sort(vec.begin(), vec.end(), [](int a, int b) { return a > b; });  // 使用lambda表达式实现降序排序
      for (auto value : vec) {
          std::cout << value << std::endl;  // 5 4 3 2 1
      }
      
      /*
      std::sort是一个非常强大的标准库算法,用于对序列进行排序。它可以对数组、向量(std::vector)、字符串(std::string)等容器进行排序。std::sort函数定义在<algorithm>头文件中。
      */
      
      std::vector<int> vec{3, 4, 6, 7, 8};
          
      auto it = std::lower_bound(vec.begin(), vec.end(), 4);  // 找出第一个不小于4的元素,并返回指向该元素的迭代器
      std::cout << *it << std::endl;  // 输出4
      
      auto it = std::lower_bound(vec.begin(), vec.end(), 5);  // 找出第一个不小于5的元素,并返回指向该元素的迭代器
      std::cout << *it << std::endl;  // 输出6
      
      auto it = std::upper_bound(vec.begin(), vec.end(), 4);  // 找出第一个大于4的元素,并返回指向该元素的迭代器
      std::cout << *it << std::endl;  // 输出6
      
  • 运算符重载

    • 赋值运算符 =:用于将一个向量的内容赋值给另一个向量

    • 比较运算符 ==、!=、<、>、<=、>=:用于比较两个向量的内容

      std::vector<int> vec1 = {1, 2, 3};
      std::vector<int> vec2;
      vec2 = vec1;  // 将vec1的内容赋给vec2
      for (auto value : vec2) {
          std::cout << value << std::endl;  // 1 2 3
      }
      
      std::vector<int> vec1 = {1, 2, 3};
      std::vector<int> vec2 = {1, 2, 4};
      bool res = vec1 <= vec2;
      std::cout << res << std::endl;  // 输出1
      
  • 特殊操作

    • emplace(const_iterator pos, Args&&… args):在指定位置就地构造一个元素,它可以直接将参数传给构造函数,而不需要提前创建一个对象

      std::vector<int> vec = {1, 2, 3, 4, 5};
      
      vec.emplace(vec.begin(), 0);  // 在vec的第一个位置就地插入一个元素0
      for (auto value : vec) {
          std::cout << value << std::endl;  // 0 1 2 3 4 5
      }