C++核心编程之Vector容器

news/2025/3/5 14:29:39/

C++ vector容器详解:从基础到实战应用

引言

        在C++ STL(标准模板库)中,vector是最常用的动态数组容器之一。它结合了数组的高效随机访问和动态扩展的灵活性,是处理动态数据集合的利器。本文将深入剖析vector的各个核心功能,并通过代码示例和扩展分析,帮助读者全面掌握其使用方法及注意事项。

1. vector基本概念

功能
        vector 是一个动态数组,支持在尾部高效插入和删除元素,同时允许随机访问元素。它的数据结构和数组非常类似,也称为单端数组

与普通数组的区别:

  • 普通数组大小固定,而vector动态扩展
  • 动态扩展机制:当容量不足时,vector会申请更大的内存空间(通常是原容量的2倍),拷贝原有元素并释放旧空间。这一过程会导致迭代器、指针和引用失效。
特性普通数组vector容器
内存管理静态固定大小动态扩展
越界检查at()方法提供
功能扩展丰富的内置方法
内存效率略低(有管理开销)
使用便捷性

  • [动态扩展并不是在原空间之后续接新空间,而是找更大的内存空间,然后将原数据拷贝新空间,释放原空间。
  • [迭代器]vector的迭代器是随机访问迭代器,支持快速随机访问容器中的元素。

2. vector构造函数

2.1 常用构造函数

  • 默认构造函数vector<T> v;                            创建一个空的vector
  • 区间构造函数vector(v.begin(), v.end()); 通过另一个vector的区间来初始化。
  • 填充构造函数vector(n, elem);                     创建一个包含nelem元素的vector
  • 拷贝构造函数vector(const vector &vec);   通过另一个vector来初始化。

2.2 示例代码

#include<iostream>
using namespace std;
#include<vector>// vector 构造函数
void Print(vector<int> &v)
{   for(vector<int>::iterator it=v.begin();it!=v.end(); it++){cout<<*it<<" ";}cout<<endl;
}void test01()
{vector<int>v1;  // 默认构造  无参构造for(int i=0;i<10;i++){v1.push_back(i);}Print(v1);// 通过区间方式进行构造vector<int>v2(v1.begin(), v1.end());Print(v2);//n个elem方式构造vector<int>v3(10,100);Print(v3);// 拷贝构造vector<int>v4(v3);Print(v4);}int main()
{test01();system("pause");return 0;
}

总结:vector提供了多种构造函数,用户可以根据需要选择合适的构造方式,灵活使用即可。

3. vector赋值操作

        给vector容器进行赋值

3.1 常用赋值操作

  • 重载等号操作符vector& operator=(const vector &vec);
  • assign函数assign(beg, end); 或 assign(n, elem);

3.2 示例代码

#include<iostream>
using namespace std;
#include<vector>// vector 赋值操作
/*
* vector& operator=(const vector &vec); //重载等号操作符
* assign(beg, end);                     //将[beg, end)区间中的数据拷贝赋值给本身。
* assign(n, elem);                      //将n个elem拷贝赋值给本身。
*/void Print(vector<int> &v)
{   for(vector<int>::iterator it=v.begin();it!=v.end(); it++){cout<<*it<<" ";}cout<<endl;
}void test01()
{vector<int>v1;for(int i=0; i<10; i++){v1.push_back(i);}Print(v1);// 赋值vector<int>v2;v2 = v1;Print(v2);// assignvector<int>v3;v3.assign(v1.begin(), v1.end());Print(v3);// n个elem 方式赋值vector<int>v4;v4.assign(10, 100);Print(v4);}int main()
{test01();system("pause");return 0;
}

内存变化说明:

  • 赋值操作会完全替换原有内容
  • 可能触发容量自动调整
  • 旧元素会被正确销毁

总结:vector的赋值操作简单直观,用户可以使用operator=assign函数进行赋值。

4. vector容量和大小操作

        对vector容器的容量和大小操作。

4.1 常用函数

  • empty():判断容器是否为空。
  • capacity():返回容器的容量。
  • size():返回容器中元素的个数。
  • resize(int num):重新指定容器的长度为num,若容器变长,则以默认值填充新位置;如果容器变短,则末尾超出容器长度的元素被删除。
  • resize(int num, elem):重新指定容器的长度为num,若容器变长,则以elem值填充新位置;如果容器变短,则末尾超出容器长度的元素被删除。

4.2 示例代码

#include<iostream>
using namespace std;
#include<vector>// vector 容量和大小void Print(vector<int> &v)
{for(vector<int>::iterator it=v.begin(); it!=v.end(); it++){cout<<*it<<" ";}cout<<endl;
}void test01()
{vector<int>v1;for(int i=0; i<10; i++){v1.push_back(i);}    Print(v1);// 判断容器是否为空if(v1.empty())   // 为真,代表容器为空{cout<<"v1为空"<<endl;}else{cout<<"v1不为空"<<endl;cout<<"v1的容量为:"<<v1.capacity()<<endl;cout<<"v1的大小为:"<<v1.size()<<endl; }// 重新指定大小v1.resize(15, 11);  //利用重载版本,可以指定默认填充    Print(v1);      //如果重新指定的比原来长了,默认用0填充新的位置v1.resize(5);Print(v1);      //如果重新指定的比原来短了,超出的部分会删除
}int main()
{test01();system("pause");return 0;
}

容量与大小的区别:

  • size:当前元素数量(逻辑大小)
  • capacity:实际分配的内存容量(物理大小)

总结:

  • 判断是否为空  --- empty
  • 返回元素个数  --- size
  • 返回容器容量  --- capacity
  • 重新指定大小  ---  resize

5. vector插入和删除操作

5.1 常用函数

  • push_back(ele);                                        //尾部插入元素ele
  • pop_back();                                              //删除最后一个元素
  • insert(const_iterator pos, ele);                 //迭代器指向位置pos插入元素ele
  • insert(const_iterator pos, int count,ele);  //迭代器指向位置pos插入count个元素ele
  • erase(const_iterator pos);                       //删除迭代器指向的元素
  • erase(const_iterator start, const_iterator end);//删除迭代器从start到end之间的元素
  • clear();                                                             //删除容器中所有元素

5.2 示例代码

#include<iostream>
using namespace std;
#include<vector>
// vector 插入和删除
/*push_back(ele);                                  //尾部插入元素elepop_back();                                      //删除最后一个元素insert(const_iterator pos, ele);                 //迭代器指向位置pos插入元素eleinsert(const_iterator pos, int count,ele);       //迭代器指向位置pos插入count个元素eleerase(const_iterator pos);                       //删除迭代器指向的元素erase(const_iterator start, const_iterator end); //删除迭代器从start到end之间的元素clear();                                         //删除容器中所有元素
*/
void Print(vector<int> &v)
{for(vector<int>::iterator it=v.begin(); it!=v.end();it++){cout<<*it<<" ";}cout<<endl;
}void test01()
{vector<int>v1;// 尾插法v1.push_back(9);v1.push_back(10);v1.push_back(34);v1.push_back(98);v1.push_back(135);Print(v1);// 尾删法v1.pop_back();Print(v1);// 插入     第一个参数是迭代器v1.insert(v1.begin(), 99);Print(v1);v1.insert(v1.begin(), 3, 100);Print(v1);// 删除   参数是迭代器v1.erase(v1.begin());Print(v1);// 清空// v1.erase(v1.begin(), v1.end());v1.clear();Print(v1);}int main()
{test01();system("pause");return 0;
}

性能注意事项:

  1. 尾部操作时间复杂度O(1)
  2. 中间插入/删除为O(n)
  3. 频繁插入建议使用list

总结:

  • 尾插  --- push_back
  • 尾删  --- pop_back
  • 插入  --- insert    (位置迭代器)
  • 删除  --- erase  (位置迭代器)
  • 清空  ---  clear  

6. vector数据存取

6.1 常用函数

  • at(int idx):返回索引idx所指的数据。
  • operator[]:返回索引idx所指的数据。
  • front():返回容器中第一个元素。
  • back():返回容器中最后一个元素。

6.2 示例代码

#include<iostream>
using namespace std;
#include<vector>// vector 存取void test01()
{vector<int>v1;for(int i=0; i<10; i++){v1.push_back(i);}// 利用[]方式访问数组中元素for(int i=0; i<v1.size();i++){cout<<v1[i]<<" ";}cout<<endl;// 利用at方式访问数组中元素for(int i=0;i<v1.size();i++){cout<<v1.at(i)<<" ";}cout<<endl;// 获取第一个元素cout<<"第一个元素为:"<<v1.front()<<endl;// 获取最后一个元素cout<<"最后一个元素为:"<<v1.back()<<endl;}int main()
{test01();system("pause");return 0;
}

7. vector互换容器

7.1 功能

swap(vec)函数用于交换两个vector容器的内容。

7.2 示例代码

#include<iostream>
using namespace std;
#include<vector>void Print(vector<int> &v)
{for(vector<int>::iterator it=v.begin(); it!=v.end();it++){cout<<*it<<" ";}cout<<endl;
}// vector 互换容器
// 1.基本使用
void test01()
{vector<int>v1;for(int i=0; i<10;i++){v1.push_back(i);}cout<<"交换前:"<<endl;Print(v1);vector<int>v2;for(int i=10; i>0;i--){v2.push_back(i);}Print(v2);cout<<"交换后:"<<endl;v1.swap(v2);Print(v1);Print(v2);}
// 2.实际用途
// 巧用swap可以收缩内存空间
void test02()
{vector<int>v;for(int i=0;i<100000;i++){v.push_back(i);}cout<<"v的容量为:"<<v.capacity()<<endl;cout<<"v的大小为:"<<v.size()<<endl;v.resize(10);  // 重新指定大小cout<<"v的容量为:"<<v.capacity()<<endl;cout<<"v的大小为:"<<v.size()<<endl;// 巧用swap收缩内存vector<int>(v).swap(v);cout<<"v的容量为:"<<v.capacity()<<endl;cout<<"v的大小为:"<<v.size()<<endl;}int main()
{test01();test02();system("pause");return 0;
}

总结:swap可以使两个容器互换,可以达到实用的收缩内存效果。

7.3 内存收缩(swap技巧)

vector<int>(v).swap(v); // 匿名对象技巧

原理分析:

  1. 创建临时匿名vector对象
  2. 利用拷贝构造函数进行元素拷贝
  3. 交换新旧vector的内容
  4. 临时对象析构释放多余内存

8. vector预留空间

8.1 功能

reserve(int len)函数用于预留容器的容量,减少动态扩展的次数。

8.2 示例代码

#include<iostream>
using namespace std;
#include<vector>
// vector 预留空间void test01()
{vector<int>v;// 利用reserve预留空间v.reserve(100000);int num = 0;  // 统计开辟次数int *p = NULL;for(int i=0; i<100000; i++){v.push_back(i);if(p!=&v[0]){p = &v[0];num++;}}cout<<"num = "<< num<<endl;}int main()
{test01();system("pause");return 0;
}

8.3 预分配内存(reserve)

v.reserve(100000); // 预先分配内存

适用场景:

  • 已知元素大致数量时
  • 需要大量push_back操作时
  • 避免频繁内存重新分配

总结:reserve函数可以有效减少vector在动态扩展时的内存分配次数,提升性能。如果数据量较大,可以一开始利用reserve预留空间。

9. 实战应用技巧

9.1 性能优化

  • 尽量使用emplace_back代替push_back
  • 预分配足够内存减少扩容次数
  • 排序时考虑使用算法库的sort
  • 避免在循环中反复resize

9.2 常见陷阱

  1. 迭代器失效问题

    • 插入/删除操作可能导致迭代器失效
    • 解决方案:操作后重新获取迭代器
  2. 越界访问

    // 错误示例
    for(int i=0; i<=v.size(); ++i) cout << v[i]; // 可能越界
    
  3. 浅拷贝问题

    vector<string*> v1;
    vector<string*> v2 = v1; // 指针拷贝,非深拷贝

9.3 最佳实践

  • 遍历优先使用迭代器或范围for循环
  • 需要逆序访问时使用rbegin/rend
  • 结合算法库使用能提升效率
  • 大数据量处理时考虑内存预分配

10. 与其他容器对比

特性vectordequelist
内存结构单端连续多段连续非连续
随机访问O(1)O(1)O(n)
头部插入O(n)O(1)O(1)
尾部插入O(1)O(1)O(1)
中间插入O(n)O(n)O(1)
迭代器类型随机访问随机访问双向
内存预分配支持支持不支持

选型建议:

  • 需要随机访问 → vector/deque
  • 频繁中间插入 → list
  • 内存敏感场景 → array
  • 先进先出队列 → deque

11. 总结与展望

        vector作为STL中最基础的容器,其高效的内存管理和便捷的操作接口使其成为C++开发者的首选容器。理解其底层实现机制对于编写高性能代码至关重要。关键要点总结:

  1. 动态扩展机制:理解capacity和size的关系
  2. 迭代器失效:掌握容器修改后的正确使用方式
  3. 内存优化:合理使用reserve和swap技巧
  4. 算法结合:善用STL算法提升开发效率

        随着C++标准的演进,vector的功能也在不断增强。C++17引入的emplace系列方法、C++20的范围操作等新特性,都使得vector的使用更加高效和安全。建议持续关注STL的发展动态,及时掌握新特性的使用方法。


http://www.ppmy.cn/news/1576836.html

相关文章

MoE 架构:专家齐聚,智启未来 —— 解锁传统稠密模型的瓶颈

MoE 架构&#xff1a;专家齐聚&#xff0c;智启未来 —— 解锁传统稠密模型的瓶颈 在人工智能和大规模模型的不断演进中&#xff0c;MoE&#xff08;Mixture of Experts&#xff0c;专家混合&#xff09;架构作为一种新兴的模型设计理念&#xff0c;正逐步吸引研究者和工程师的…

kotlin中reified如何实现真泛型

reified 是 Kotlin 的「泛型透视镜」&#xff0c;配合 inline 函数&#xff0c;让泛型在运行时保留类型信息&#xff0c;直接看到具体的类型&#xff08;不再被类型擦除蒙蔽双眼&#xff09;。 举个现实例子&#xff1a; 假设你要写一个函数&#xff0c;判断某个对象是否是 St…

供应链管理:质量屋HQ / House of Quality

在供应链管理中&#xff0c;质量屋&#xff08;House of Quality, HOQ&#xff09;是一种重要的质量管理工具&#xff0c;它源于质量功能配置&#xff08;Quality Function Deployment, QFD&#xff09;理论&#xff0c;用于将顾客需求转化为产品或服务的技术要求&#xff0c;从…

477页pdf深度剖析!张潼教授的《机器学习算法数学分析》

在当今数字化时代&#xff0c;机器学习已成为解决复杂问题的关键技术。从数据分析到人工智能&#xff0c;机器学习的应用无处不在。 然而&#xff0c;尽管机器学习算法在实践中取得了显著成功&#xff0c;其背后的数学理论却往往散布在广泛的文献中。张潼教授的新书《机器学习…

从 JVM 源码(HotSpot)看 synchronized 原理

大家好&#xff0c;我是此林。 不知道大家有没有这样一种感觉&#xff0c;网上对于一些 Java 框架和类的原理实现众说纷纭&#xff0c;看了总是不明白、不透彻。常常会想&#xff1a;真的是这样吗&#xff1f; 今天我们就从 HotSpot 源码级别去看 synchronized 的实现原理。全…

MyBatis-Plus 逻辑删除实现

在很多企业级应用中&#xff0c;数据删除操作通常采用 逻辑删除 的方式&#xff0c;而不是物理删除。逻辑删除指的是通过更新字段&#xff08;例如 is_deleted 或 status&#xff09;来标记数据为删除状态&#xff0c;而不是真的从数据库中删除记录。这样做的好处是保留数据的历…

网线水晶头接法

目录 介绍 排线标准 连接方法 直连互联法 交叉互联法 操作步骤 介绍 网线&#xff1a;双绞线&#xff0c;有4对8条芯线&#xff0c;分别为白绿色、绿色、白橙色、蓝色、白蓝色、橙色、白棕色、棕色。 水晶头&#xff1a;也被称为RJ45连接器&#xff0c;是一种用于网络连接…

【应急响应工具教程】一款自动化分析网络安全应急响应工具--FindAll

1、工具介绍 FindAll 是一款安全团队开发的轻量化蓝队工具&#xff0c;专为应急响应场景设计&#xff0c;主打信息收集与威胁情报联动&#xff0c;尤其适合团队快速排查多台主机的安全风险。同时FindAll采用客户端-服务器&#xff08;CS&#xff09;架构&#xff0c;特别适用于…