C++ 学习系列 -- std::vector (未完待续)

一  std::vector 是什么?

   vector 是c++ 中一种序列式容器,与前面说的 array 类似,其内存分配是连续的,但是与 array 不同的地方在于,vector 在运行时是可以动态扩容的,此外 vector 提供了许多方便的操作,比如:插入、删除、查找、排序等。

std::vector - cppreference.com

二  std::vector 的特性与常见面试问题有哪些?

1  特性

1.1  vector 底层是基于分配连续空间的数组,因此 vector的访问既可以通过 容器的迭代器的方式,也可以通过数组的下标方式来访问元素

1.2 vector 可以在运行期间根据需要,进行动态的扩容,当已分配的空间被用满时,会再重新分配一块通常是原来空间两倍的内存,接下来依次将原来内存中的元素依次拷贝过来

1.3 基于底层的原理 ,其相关操作的时间复杂度如下:

       1)访问元素的时间复杂度 与数组相同,是 O(1) ;

       2) 在 vector末尾插入元素或者删除元素 O(1)

       3)   在 vector 中间某个位置插入或者删除一个元素,O(n)

2 常见面试问题

1. vector 的扩容机制 ?

  1.1  扩容倍数一般为 2 倍或者 1.5 倍
// main.cpp#include<vector>
#include<iostream>int main()
{std::vector<int>  vec2;for(int i=0; i<20; i++){std::cout << "size: " << vec2.size() << ", capaticy: " << vec2.capacity() << std::endl;vec2.push_back(i);}return 0;
}

其实际存储的元素个数,与开辟空间可以存放的元素个数如下:

从输出结果可以看出,每当存放的元素将分配的空间耗尽以后,vector 就会再次申请两倍于当前内存大小的空间来使用

   1.2  扩容的具体过程

        当存放元素个数将 vector 分配的内存空间耗尽时,当再放入新元素时,会触发如下过程

  •  重新分配新的连续内存空间,大小一般是原来的 2 倍
  •  将原来内存空间的元素依次拷贝到新空间中, 此过程会触发拷贝构造函数
  •  调用存放在原内存空间中各元素的析构函数
  •  释放掉原空间的内存 

  我们可以通过如下代码的结果看到这个过程

#include<iostream>
#include<vector>class AAA
{
public:AAA(int i):index(i){std::cout << "constructor AAA index: " << index << std::endl;}~AAA(){std::cout << "destructor AAA index: " << index << std::endl;}AAA(AAA& a):index(a.index){std::cout << "copy constructor AAA index: " << index << std::endl;}AAA(const AAA& a):index(a.index){std::cout << "copy constructor const AAA index: " << index << std::endl;}AAA(AAA&& a):index(a.index){std::cout << "move copy constructor AAA index: " << index << std::endl;a.index = 0;}AAA& operator=(AAA& a){this->index = a.index;std::cout << "equals AAA index: " << index << std::endl;return *this;}public:int index;
};int  main()
{std::vector<AAA> vec2;for(int i=0; i<7; i++){std::cout << "size: " << vec2.size() << ", capaticy: " << vec2.capacity() << std::endl;vec2.emplace_back(i);}return 0;
}

输出:

2.如何避免扩容导致的效率低下?

    2.1  可以在使用 vector 之前,预估好元素的个数,在使用 vector 之前,将内存空间分配好,而不是在一边插入一边分配新空间与拷贝旧空间的元素

   2.2  若是无法预估,可以在自定义类中实现高效的移动构造函数,然后禁用拷贝构造函数,这样vector 在扩容完毕后的拷贝元素阶段,会自动调用移动构造函数,具体如下:

(c++ 中声明移动构造函数后,会自动禁用拷贝构造函数)

#include<iostream>
#include<vector>class AAA
{
public:AAA(int i):index(i){std::cout << "constructor AAA index: " << index << std::endl;}~AAA(){std::cout << "destructor AAA index: " << index << std::endl;}AAA(AAA&& a):index(a.index){std::cout << "move copy constructor AAA index: " << index << std::endl;a.index = 0;}AAA& operator=(AAA& a){this->index = a.index;std::cout << "equals AAA index: " << index << std::endl;return *this;}public:int index;
};int  main()
{std::vector<AAA> vec2;for(int i=0; i<7; i++){std::cout << "size: " << vec2.size() << ", capaticy: " << vec2.capacity() << std::endl;vec2.emplace_back(i);}return 0;
}

输出: 

3.为什么选择以1.5倍或者2倍方式进行扩容?而不是3倍4倍扩容?

    面试题:C++vector的动态扩容,为何是1.5倍或者是2倍_vector扩容_森明帮大于黑虎帮的博客-CSDN博客

4.vs为什么选择1.5倍,linux为什么选择2倍?

面试题:C++vector的动态扩容,为何是1.5倍或者是2倍_vector扩容_森明帮大于黑虎帮的博客-CSDN博客

三 std::vector 的使用

  1. vector 常见接口与操作

       std::vector - cppreference.com

     1.1  容器构造

构造函数说明
vector();空构造函数

template<typename T>

vector( size_type count,

                 const T& value);

构造 count 各 value 到vector 中
template< class InputIt >

vector( InputIt first, InputIt last,

        const Allocator& alloc = Allocator() );
利用迭代器构造 vector 
vector( const vector& other );拷贝构造函数
vector( vector&& other );移动构造函数

       使用例子:

          

#include<vector>
#include<iostream>void printVector(std::vector<T>& vec)
{for(T& v:vec){std::cout << v << " ";}std::cout << "" << std::endl;
}void testVector()
{std::cout << "testVector --" << std::endl;// 1. 构造空 vectorstd::vector<int>  vec1;for(int i=1;i<7;i++){vec1.push_back(i);}std::cout << "------------ 1" << std::endl;printVector(vec1);// 2. 构造 count 个 value 到 vector 中std::vector<int> vec2(6, 88);std::cout << "------------ 2" << std::endl;printVector(vec2);// 3. 利用迭代器构造 vectorstd::vector<int> vec3(vec2.begin(), vec2.end());std::cout << "------------ 3" << std::endl;printVector(vec3);// 4. 拷贝构造函数std::vector<int> vec4(vec1);std::cout << "------------ 4" << std::endl;printVector(vec4);// 5. 移动构造函数std::vector<int> vec5(std::move(vec1));std::cout << "------------ 5" << std::endl;printVector(vec5);
}int main(int argc, char *argv[])
{testVector();return 0;
}

输出:

  

     1.2  容器访问

函数说明
at(size_type pos)返回位置 pos 上的元素引用
operator[size_type pos]同上
front返回 vector 上第一个元素的引用
back返回 vector 上最后一个元素的引用
data返回底层存储数组的指针

      示例代码:

#include<iostream>
#include<vector>void testVector2()
{std::cout << "testVector --" << std::endl;// 构造 vectorstd::vector<int>  vec1;for(int i=1;i<7;i++){vec1.push_back(i);}std::cout << "------------ 1" << std::endl;// 1. atint pos = 3;std::cout << " pos = 3, val = " << vec1.at(pos)<< std::endl;// 可以修改数据int& tmp1 = vec1.at(pos);tmp1 = 88;std::cout << " pos = 3, val = " << vec1.at(pos)<< std::endl;std::cout << "------------ 2" << std::endl;// 2. operator[]std::cout << " pos = 3, val = " << vec1[pos]<< std::endl;std::cout << "------------ 3" << std::endl;// 3. frontstd::cout << " front, val = " << vec1.front() << std::endl;std::cout << "------------ 4" << std::endl;// 4. backstd::cout << " back, val = " << vec1.back()<< std::endl;std::cout << "------------ 5" << std::endl;// 5. dataint*  tmp5 = vec1.data();for(int i=0; i<6;i++){std::cout << "i = " << i <<", val = " <<*(tmp5 + i) << std::endl;}
}int main()
{testVector2();return 0;
}

输出:

  

    1.3  容器空间

函数说明
empty()vector 是否为空
size()返回存储的元素个数
reserve(size_type new_cap)提升vector 容量
capacity()返回vector分配的空间可以存放的元素个数

  示例如下

#include<iostream>
#include<vector>void testVector3()
{std::cout << "testVector --" << std::endl;std::vector<int> vec1;std::cout << " empty: " << vec1.empty() << ", size: " << vec1.size() << std::endl;for(int i=0; i<5; i++){std::cout << "size: " << vec1.size() << ", capaticy: " << vec1.capacity() << std::endl;vec1.emplace_back(i+1);}std::cout << " empty: " << vec1.empty() << ", size: " << vec1.size() << std::endl;vec1.reserve(8);std::cout << "size: " << vec1.size() << ", capaticy: " << vec1.capacity() << std::endl;
}int main()
{testVector3();return 0;
}

输出:

1.4  容器修改

      

函数说明
clear()清空 vector 中的所有元素
insert在位置 pos 前插入元素 value
emplace与insert类似,不过该函数可以只传元素类的构造参数,实现原地构造,效率上比 insert 高一些,因为缺少了拷贝函数的调用
push_back在 vector 的最后append 新的元素,若是append前,vector 的size与capacity相等,那么就会重新分配内存
emplace_back与 push_back 类似,区别在于该函数可以只传元素类的构造参数,实现原地构造,效率上比 push_back 高一些,因为缺少了拷贝函数的调用
pop_back将 vector 的最后一个元素移除

      示例如下:

         

#include<iostream>
#include<vector>void testVector4()
{std::cout << "testVector --" << std::endl;std::vector<int> vec1;for(int i=0; i<5; i++){vec1.emplace_back(i+1);}printVector(vec1);// 1. insertstd::cout << "------------ 1" << std::endl;auto iter = std::find(vec1.begin(), vec1.end(), 3);vec1.insert(iter,666);printVector(vec1);// 2. emplacestd::cout << "------------ 2" << std::endl;vec1.emplace(iter,888);printVector(vec1);// 3. push_backstd::cout << "------------ 3" << std::endl;vec1.push_back(77);printVector(vec1);// 4. emplace_backstd::cout << "------------ 4" << std::endl;vec1.emplace_back(778);printVector(vec1);// 5. pop_backstd::cout << "------------ 5" << std::endl;vec1.pop_back();printVector(vec1);// 6. clearstd::cout << "------------ 6" << std::endl;vec1.clear();std::cout << "empyt: " << vec1.empty() << std::endl;
}int main()
{testVector4();return 0;
}

 输出:

 

四  std::vector 的简单实现

  接下来我们实现自己简单的  vector 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/140231.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

1小时掌握Python操作Mysql数据库之pymysql模块技术

大家好&#xff0c;我是python222小锋老师。前段时间卷了一套 Python3零基础7天入门实战 近日锋哥又卷了一波课程&#xff0c;Python操作Mysql数据库的pymysql技术&#xff0c;文字版视频版。1小时掌握。 视频版教程 1小时掌握Python操作Mysql数据库之pymysql模块技术 文字版…

Rust vs C++ 深度比较

Rust由于其强大的安全性受到大量关注&#xff0c;被认为C在系统编程领域最强大的挑战者。本文从语言、框架等方面比较了两者的优缺点。原文: Rust vs C: An in-depth language comparison Rust和C的比较是开发人员最近的热门话题&#xff0c;两者之间有许多相似之处&#xff0c…

使用FastChat部署Baichuan2

1. 引言 近来&#xff0c;大型语言模型的市场需求呈现出蓬勃发展的态势。然而&#xff0c;仅仅掌握模型的数据准备和训练是不够的&#xff0c;模型的部署方法也变得至关重要。在这篇文章中&#xff0c;我们将以Baichuan2为例&#xff0c;利用FastChat进行模型部署的实战操作。…

两种常见矩形框旋转方法推导及其C++实现

在已知矩形中心点、长宽和旋转角度&#xff08;定义为矩形最长边与X轴正方向的夹角&#xff09;&#xff0c;如何确定矩形四个顶点的坐标&#xff0c;通常有以下两种处理方法。 法一&#xff1a;直接对顶点进行旋转 比如下图虚线框矩形是实线框矩形绕矩形中心点旋转后得到。在…

深度学习实战基础案例——卷积神经网络(CNN)基于Xception的猫狗识别|第2例

文章目录 一、环境准备二、数据预处理三、构建模型四、实例化模型五、训练模型5.1 构建训练函数5.2 构建测试函数5.3 开始正式训练 六、可视化精度和损失七、个体预测总结 今天使用轻量级的一个网络Xception做一个简单的猫狗识别案例&#xff0c;我的环境具体如下&#xff1a; …

记一次STM32F4 HAL IAP开发过程踩坑

第一次在HAL库上做IAP&#xff0c;不太熟悉库结构&#xff0c;被坑了一早上… MCU上做了一个shell&#xff0c;实现了goto命令跳转到APP区执行&#xff08;只是为了开发时方便&#xff09;。跳转到APP前和以前一样清理了所有初始化过的外设&#xff0c;也对中断进行了处理&…

MySQL数据库的索引和事务

目录 一、索引 1.1Mysql索引 1.2索引的作用 1.3 创建索引的依据 1.4 普通索引 修改表方式创建索引 删除索引 1.5 唯一索引 修改表方式创建 删除索引 1.6 主键索引 修改表方式创建 1.7 组合索引 1.8 全文索引 1.9查看索引 二、事务 2.1事务概念 2.2事务的ACID特…

rocketmq-spring-boot-starter 2.1.0 事务消息移除参数txProducerGroup

statrer引入 <dependency><groupId>org.apache.rocketmq</groupId><artifactId>rocketmq-spring-boot-starter</artifactId><version>2.2.3</version></dependency> starter 2.0.2对应rocketmq 4.4.0 starter 2.1.0对应rocke…

NSDT 3D孪生场景搭建:阵列摆放详解

阵列摆放概念 阵列摆放是指将物体、设备或元件按照一定的规则和间距排列组合的方式。在工程和科学领域中&#xff0c;阵列式摆放常常用于优化空间利用、提高效率或增强性能。 阵列摆放通常需要考虑间距、角度、方向、对称性等因素&#xff0c;以满足特定的要求和设计目标。不同…

Seata流程源码梳理下篇-TC

我们上篇简单梳理了下TM、RM的一些流程&#xff08;离现在过得挺久的了&#xff0c;这篇我们这篇来梳理下TC的内容。 TC (Transaction Coordinator) - 事务协调者 维护全局和分支事务的状态&#xff0c;驱动全局事务提交或回滚。 TM (Transaction Manager) - 事务管理器 定…

C++ Primer 第5章 语句

C Primer 第5章 语句 5.1 简单语句一、空语句二、别漏写分号&#xff0c;也别多写分号三、复合语句&#xff08;块&#xff09; 5.2 语句作用域5.3 条件语句5.3.1 if语句一、使用if else语句二、嵌套if语句三、注意使用花括号四、悬垂else五、使用花括号控制执行路径 5.3.2 swi…

Oracle分区的使用详解:创建、修改和删除分区,处理分区已满或不存在的插入数据,以及分区历史数据与近期数据的操作指南

一、前言 什么是表分区: Oracle的分区是一种将表或索引数据分割为更小、更易管理的部分的技术。它可以提高查询性能、简化维护操作,并提供更好的数据组织和管理。 表分区和表空间的区别和联系: 在Oracle数据库中,表空间(Tablespace)是用于存储表、索引和其他数据库对…

Baichuan2 技术报告笔记

文章目录 预训练预训练数据模型架构TokenizerPositional EmbeddingsAcitivations and NormalizationsOptimizations 对齐Supervised Fine-TuningRLHF 安全性预训练阶段对齐阶段 参考资料 对Baichuan2技术报告阅读后的笔记 Baichuan2 与其他大模型的对比如下表 预训练 预训练数…

【Java开发】Redis位图实现统计日活周活月活

最近研究了使用 Redis 的位图功能统计日活周活等数据&#xff0c;特来和大家分享下&#xff0c;Redis 位图还可用于记录用户签到情况、判断某个元素是否存在于集合中等。 1 Redis 位图介绍 Redis 位图是一种特殊的数据结构&#xff0c;它由一系列位组成&#xff0c;每个位只能…

洛谷P8815:逻辑表达式 ← CSP-J 2022 复赛第3题

【题目来源】https://www.luogu.com.cn/problem/P8815https://www.acwing.com/problem/content/4733/【题目描述】 逻辑表达式是计算机科学中的重要概念和工具&#xff0c;包含逻辑值、逻辑运算、逻辑运算优先级等内容。 在一个逻辑表达式中&#xff0c;元素的值只有两种可能&a…

Appilot发布:打造面向DevOps场景的开源AI助手

今日&#xff0c;数澈软件Seal &#xff08;以下简称“Seal”&#xff09;宣布推出面向 DevOps 场景的 AI 助手 Appilot&#xff0c;这款产品将充分利用 AI 大语言模型的能力为用户提供变革性的部署和应用管理体验。Seal 此次发布的 Appilot 项目&#xff0c;可以让用户直接输入…

leetcode 22. 括号生成

2023.9.24 看到组合两个字&#xff0c;想到了回溯。 大致思路是将所有可能的组合列出来&#xff0c;通过中止条件筛选掉无效的括号。 第一个中止条件&#xff1a;如果右括号数量大于左括号&#xff0c;那括号肯定无效。 第二个中止条件&#xff1a;当左右括号数量相等&#x…

古代有没有电子元器件?

手机&#xff0c;电脑&#xff0c;电视等等电子产品&#xff0c;无时无刻充斥在我们的生活中&#xff0c;如果有一天突然没有了这些功能多样的电子产品&#xff0c;估计大部分人都会一时之间难以适应。 这就好比正在上网&#xff0c;结果突然被人断了网&#xff0c;导致无网络连…

Go语言入门篇

目录 一、基础数据类型 1.1 变量的定义方式 1.2 用%T输出变量的类型 二、复合数据类型 2.1 数组 2.1.2、数组的遍历 2.1.3 数组传参 2.2. 切片slice 2.2.1. 初始化切片 2.2.2. append向切片中追加元素 2.2.3. 切片的截取 2.3. map 2.3.1. map初始化 2.3.2. 添加和…

操作系统权限提升(三十)之数据库提权-SQL Server sp_oacreate+sp_oamethod(dba权限)提权

SQL Server sp_oacreate+sp_oamethod(dba权限)提权 sp_oacreate+sp_oamethod介绍 在xp_cmdshell被删除或不能利用是可以考虑利用sp_oacreate,利用前提需要sqlserver sysadmin账户服务器权限为system(sqlserver2019默认被降权为mssql)。sp_oacreate 是一个存储过程,可以…