当前位置: 首页 > news >正文

【C++】vector常用接口的使用

欢迎拜访Madison-No7个人主页
文章主题 探秘string的底层实现
隶属专栏我的 C++ 成长日志
写作日期2025年10月12日

目录

一、什么是vector

二、vector常见接口的使用

2.1 构造函数

🌸无参构造:vector()

🌸有参构造:

🌸拷贝构造:

🌸 使用迭代器进行初始化构造:

2.2 vector iterator的使用:

🎯begin+end:

2.3 vecror空间增长的相关接口:

🎯size:获取数据个数

🎯capacity:获取容量大小

🎯empty:判断是否为空

🎯resize:改变vector的size

🎯reserve:改变vector的capacity

2.4 vector的增删查改:

 🌊 push_back:尾插

 🌊 pop_back:尾删

 🌊 insert:在position之前插入val

🌟问题:vector 可以替代 string 嘛?

 🌊 erase:删除position位置的数据

 🌊 swap:交换两个vector的数据空间

 🌊 operator[] :像数组一样访问

三、使用vector需注意的点:


一、什么是vector

  1. vector 是一种序列容器,用于表示大小可动态变化的数组。

  2. 与普通数组类似,vector 的元素也存储在连续的内存空间中。这意味着可以通过元素的常规指针偏移量来访问 vector 中的元素,其访问效率与普通数组完全一致。但与普通数组不同的是,vector 的大小可以动态改变,其内存空间由容器自动管理。

  3. 在内部实现上,vector 会使用动态分配的数组来存储元素。当插入新元素导致 vector 需要扩容时,容器可能会重新分配内存 —— 即分配一块新的数组空间,并将所有元素移动到新空间中。这种操作的时间开销相对较大,因此 vector 并不会在每次添加元素时都执行重新分配。

  4. 相反,vector 容器会预先分配额外的内存空间以应对可能的扩容需求。因此,容器的实际容量(capacity)可能会大于存储当前元素所需的内存空间(即容器的大小 size)。不同的标准库实现可能会采用不同的扩容策略,以在内存占用和重新分配频率之间取得平衡。但无论采用何种策略,重新分配都只会在大小以对数级增长的间隔时发生,从而确保在 vector 末尾插入单个元素的操作能达到均摊常数时间复杂度。

  5. 因此,与普通数组相比,vector 会消耗更多内存,但换来的是更便捷的内存管理能力和高效的动态扩容能力。

  6. 与其他动态序列容器(deque 双端队列、list 双向链表、forward_list 单向链表)相比:

  • vector 在元素访问上效率极高(与普通数组一致);
  • 末尾添加或删除元素的操作上效率也相对较高;
  • 但在非末尾位置插入或删除元素时,效率远低于其他容器;
  • 此外,其迭代器和引用的稳定性也不如 list 和 forward_list(元素移动可能导致迭代器 / 引用失效)。

二、vector常见接口的使用
 

2.1 构造函数

🌸无参构造:vector()

表示创建一个空的 vector 容器,即不包含任何元素,仅初始化容器自身的基础结构。

🌸有参构造:

表示构造并初始化n个val适用于需要批量初始化相同值元素的场景。

// T:vector存储的元素类型;n:元素数量(非负整数);val:初始化值(T类型)
std::vector<T> 容器名(n, val);

🌸拷贝构造:

🌸 使用迭代器进行初始化构造:

2.2 vector iterator的使用:


🎯begin+end:

表示获取第一个数据位置的iterator/const_iterator, 获取最后一个数据的下一个位置的iterator/const_iterator

遍历vector:

2.3 vecror空间增长的相关接口:

🎯size:获取数据个数

🎯capacity:获取容量大小

capacity底层扩容机制:

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';}}
}

Vs2022下运行:

Linux下的结果:

🌟综上:capacity的代码在vsg++下分别运行会发现,vscapacity是按1.5倍增长的,g++是按2 倍增长的

🎯empty:判断是否为空

注意:如果容器大小为0,则返回True,否则返回false。

🎯resize:改变vectorsize

说明:该函数的第二个参数value_type val=value_type();其中value_type表示第一个模板参数(T),,对于vector<int>,T就是int。对于value_type()表示我们不传入第二个参数的时候,才会调用该类型的默认构造函数,对于内置类型,value_type()会生成值为0的临时对象,为什么会是0呢?而不是其他值,这是因为确保了内置类型在这种场景下的初始化行为是可预期的,也就是初始化的值是多少(0)是明确的,而不是随机内存垃圾(随机值)。对于自定义类型,会调用其默认构造函数

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

  • 如果n小于当前容器的大小(size),则内容将被减少到前n个元素,并删除超出的元素(并销毁它们)
  • 如果n大于当前容器的大小(size),则通过在末尾插入所需的元素来扩展内容,以达到n的大小。如果指定了val,则将新元素初始化为val的副本,否则将初始化为0。
  • 如果n大于当前容器容量(capacity),则会扩容。然后通过在末尾插入所需的元素来扩展内容,以达到n的大小。如果指定了val,则将新元素初始化为val的副本,否则将初始化为0。
void Print(const vector<int>& v)
{cout <<"size:" << v.size() << endl;cout << "capacity" << v.capacity() << endl;cout << "遍历数组:";vector<int>::const_iterator it = v.cbegin();while (it != v.cend()){cout << *it << " ";it++;}cout << endl;
}
void test02()
{vector<int> v2(10, 1);Print(v2);cout << "n为5:" << endl;v2.resize(5);Print(v2);cout << "n为15:" << endl;//指定了valv2.resize(15,3);Print(v2);cout << "n为20:" << endl;//没指定valv2.resize(20);Print(v2);
}

🎯reserve:改变vectorcapacity

请求更改容量,要求vector容器容量至少足以容纳n个元素。
如果n大于当前的vector容量,则该函数使容器重新分配其存储空间,将其容量增加到n(或更大)
在所有其他情况下,函数调用不会导致重新分配,向量容量也不会受到影响。
这个函数对vector的大小没有影响,也不能改变vector的元素。

🌟总结:

如果n>capacity大,就扩容(VS下要多少给多少空间),n<capacity,就不用管,也不会缩容。reserve只负责开辟空间,如果确定知道需要用多少空间,reserve可以缓解vector增容的代价缺陷问题。

比如:

如果已经确定 vector 中要存储元素的大概个数,可以reserve()提前将空间开好,这样就可以避免免一遍插入一遍扩容而导致效率低下的问题了。

void TestVectorExpand()
{size_t sz;vector<int> v;v.reserve(100);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';}}
}

2.4 vector的增删查改:

 🌊 push_back:尾插

注意:value_type指的是第一个模板参数(T)。

 🌊 pop_back:尾删

 🌊 insert:position之前插入val

void Print_arr(const vector<int>& v)
{vector<int>::const_iterator it = v.cbegin();while (it != v.cend()){cout << *it << " ";it++;}cout << endl;
}
void test03()
{vector<int> v(10,0);//在下标为5的位置前插入1v.insert(v.begin()+5, 1);Print_arr(v);//在下标为7的位置插入5个2v.insert(v.begin()+7,5,2);Print_arr(v);//在下标为9的位置插入一个迭代器区间的内容v.insert(v.begin()+9,v.begin(),v.end());Print_arr(v);
}

🌟问题:vector<char> 可以替代 string 嘛?

答案是不可以,虽然他们俩的底层本质上都是动态增长的数组,但是 string 字符串的结尾默认有 \0,可以更好的兼容 C 接口,而 vector<char> 的结尾默认是没有 \0 的,需要我们自己插入。

 🌊 erase:删除position位置的数据

 🌊 swap:交换两个vector的数据空间

void Print_arr(const vector<int>& v,const int& n)
{cout << "v"<<n<<":";vector<int>::const_iterator it = v.cbegin();while (it != v.cend()){cout << *it << " ";it++;}cout << endl;
}
void test04()
{vector<int> v1(10,1);vector<int> v2(5,0);cout << "交换前 :" << endl;Print_arr(v1,1);Print_arr(v2, 2);v1.swap(v2);cout << "交换后:" << endl;Print_arr(v1, 1);Print_arr(v2, 2);
}

 🌊 operator[] 像数组一样访问

void test05()
{vector<int> myvector(10,1);  for (int i=0;i< myvector.size();i++){myvector[i] = 2;cout << myvector[i] << " ";}cout << endl;const vector<int> myvector2(5, 5);for (int i = 0; i < myvector2.size(); i++){//只读,不可修改//myvector2[i] = 2;cout << myvector2[i] << " ";}
}

三、使用vector需注意的点:

vector里面可以存int、double、char这些自定义类型外,还可以存自定义类型。

比如:

在vector里面存string类型:

void test06()
{string s1="hello world";vector<string> v1;v1.push_back(s1);//尾插字符串,会走隐式类型转换v1.push_back("have a nice day");//底层原理是通过迭代器实现编译器会获取 v1.begin() 和 v1.end() 迭代器,//循环中通过*it解引用获取元素,拷贝给 s(每次循环都会调用string的拷贝构造)。//auto 自动推导 s 的类型为stringfor (auto s:v1){//调用 string 的输出运算符重载,打印字符串内容。cout << s << " ";}//每次循环都会调用string的拷贝构造严重影响效率//所以我们可以加上引用,减少拷贝,提高效率for (auto& s : v1){cout << s << " ";}
}

🌟小Tips:auto后面加不加&,就想s是不是自定义类型且该自定义类型还涉及到申请空间,如果是就要加&,减少深拷贝,提高效率,所以最好还是加上&。

在vector里面存vector类型:

void test08()
{//二维数组//5行10列vector<int> v (10,1);vector<vector<int>> vv(5,v);vv[2][5] = 2;//与vv[2][5] = 2等价:vv.operator[](2).operator[](5) = 2;//遍历二维数组for (int i=0;i<vv.size();i++){for (int j=0;j<v.size();j++){cout << vv[i][j] << " ";}cout << endl;}
}

🌟注意:vector不支持流插入和流提取,因为vector的格式不固定,输出的时候每个数之间有,吗?有空格吗?我们可以自己设置打印的格式,也就是从侧面实现vector的输出。


完。

今天的分享就到这里,感谢各位大佬的关注,大家互相学习,共同进步呀!

http://www.dtcms.com/a/481769.html

相关文章:

  • ES6 面试题及详细答案 80题 (62-80)-- 类与继承
  • 宁波高质量品牌网站设计厂家网络完全公司排名
  • 网站源码在线提取东莞市网站公司
  • C++中substr()函数详解
  • 乐迪信息:煤矿堆煤隐患难排查?AI摄像机实时监控与预警
  • Deep Dive into LLMs like ChatGPT 学习笔记
  • AI-大模型接入
  • 4.Windows Server 磁盘管理(1)
  • 后端三层架构
  • PyTorch 实现多模型集成与 VGG 在 CIFAR-10 上的应用
  • 网站建设所需费用明细长沙优化官网收费标准
  • NVMe高速传输之摆脱XDMA设计52: 上板资源占用率分析
  • 青岛建网站的公司有哪些wordpress能用手机管理吗
  • 2.游戏中的Buff系统设计
  • 解锁网络性能优化利器HTTP/2C
  • 基于 React + Go + PostgreSQL + Redis 的管理系统开发框架
  • Flink Checkpoint 设计理念深度解析(附源码)
  • 从 TF-IDF 到 Word2Vec:让推荐系统更懂语义
  • 01-ELK安装ES,ES-head
  • OpenCV4-直方图与傅里叶变换-项目实战-信用卡数字识别
  • 医院排班挂号系统小程序
  • 河北建设厅网站打不开是什么原因国际新闻直播
  • C++设计模式_行为型模式_命令模式Command
  • Blender自动化展UV插件 UV Factory 4.3 v1 – Powerful Modular Uv Tools
  • 网络与通信安全课程复习汇总2——信息保密
  • 密码学安全:CIA三元组与三大核心技术
  • 建网站怎么做本地的营销网站建设
  • 短剧分销系统技术拆解:渠道推广码生成、订单归因与实时分账系统实现
  • ​RocketMQ 与 RabbitMQ 全面对比:架构、性能与适用场景解析
  • RabbitMQ 消息可靠投递