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

news2024/11/25 4:35:30

一  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. 构造空 vector
    std::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. 利用迭代器构造 vector
    std::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;

    // 构造 vector
    std::vector<int>  vec1;
    for(int i=1;i<7;i++)
    {
        vec1.push_back(i);
    }
    std::cout << "------------ 1" << std::endl;

    // 1. at
    int 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. front
    std::cout << " front, val = " << vec1.front() << std::endl;

    std::cout << "------------ 4" << std::endl;
    // 4. back
    std::cout << " back, val = " << vec1.back()<< std::endl;

    std::cout << "------------ 5" << std::endl;
    // 5. data

    int*  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. insert
    std::cout << "------------ 1" << std::endl;
    auto iter = std::find(vec1.begin(), vec1.end(), 3);
    vec1.insert(iter,666);
    printVector(vec1);

    // 2. emplace
    std::cout << "------------ 2" << std::endl;
    vec1.emplace(iter,888);

    printVector(vec1);

    // 3. push_back
    std::cout << "------------ 3" << std::endl;
    vec1.push_back(77);
    printVector(vec1);

    // 4. emplace_back
    std::cout << "------------ 4" << std::endl;
    vec1.emplace_back(778);

    printVector(vec1);

    // 5. pop_back
    std::cout << "------------ 5" << std::endl;
    vec1.pop_back();
    printVector(vec1);

    // 6. clear
    std::cout << "------------ 6" << std::endl;
    vec1.clear();
    std::cout << "empyt: " << vec1.empty() << std::endl;
}

int main()
{
    testVector4();
    return 0;
}

 输出:

 

四  std::vector 的简单实现

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

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

世界前沿技术发展报告2023《世界信息技术发展报告》(四)电子信息技术

&#xff08;四&#xff09;电子信息技术 1. 概述2. 微电子技术2.1 精细制程芯片2.1.1 中国台积电发布2纳米制程工艺细节2.1.2 美国英特尔公司称2030年芯片晶体管密度将达到目前的10倍2.1.3 韩国三星电子率先实现3纳米制程芯片量产2.1.4 日本丰田、索尼等8家公司合资成立高端芯…

【李沐深度学习笔记】矩阵计算(1)

课程地址和说明 线性代数实现p4 本系列文章是我学习李沐老师深度学习系列课程的学习笔记&#xff0c;可能会对李沐老师上课没讲到的进行补充。 本节是第一篇 矩阵计算 标量导数 导数刻画的是函数在某点的瞬时变化率 这东西都是考研学过的&#xff0c;快速略过&#xff0c;如…

网站接入公网并配置域名访问【详细教程】

网站接入公网并配置域名访问【详细教程】 安装Nginx上传网页文件配置Nginx腾讯云配置域名映射接入公网备案流程 本教程将以腾讯云服务器和腾讯云域名为例&#xff0c;介绍如何快速将网站接入公网并配置域名访问。我们将使用xshell工具进行操作&#xff0c;并涵盖安装nginx、上传…

Unity之VR如何实现跟随视角的UI

前言 我们在制作VR项目的时候,大部分时候,是把UI固定到一个位置,比如桌子或者空中,这么做固然稳定,但是当我们有以下需求的时候,固定位置的UI可能会不适用: 1.场景较小,操作物体占用了很大体积,没有固定的可以清晰显示完整UI的位置。 2.需要频繁的前后左右,更换姿势…

Unity3D 使用LineRenderer自由画线

原理 一个LineRenderer是一次画线&#xff0c;需要使用对象池一帧记录一个鼠标位置 代码 这是线绘制器的代码&#xff0c;依赖于笔者写过的一个简易对象池 传送门&#xff1a;>>对象池 using EasyAVG; using System; using System.Collections.Generic; using UnityEn…

指针和数组笔试题的透析

指针---进阶篇&#xff08;三&#xff09; 一、前言二、一维数组例题透析&#xff1a;三、指针笔试题1.例一&#xff1a;2.例二&#xff1a;3.例三&#xff1a;4.例四&#xff1a;5.例五&#xff1a;6.例六&#xff1a; 一、前言 那么好了好了&#xff0c;宝子们&#xff0c;从…

王道408计组汇编语言部分学习总结

x86汇编语言指令基础 x86处理器中程序计数器PC 通常被称为IP 高级语言—>汇编语言—>机器语言 x86架构CPU&#xff0c;有哪些寄存器 EAX通用寄存器EBXECXEDXESI 变址寄存器 变址寄存器可用于线性表、字符串的处理EDIEBP堆栈基指针堆栈寄存器用于实现函数调用 ESP堆栈…

LESS的叶绿素荧光模拟实现与操作

LESS的叶绿素荧光模拟实现与操作 前情提要FLUSPECT模型荧光的三维面元冠层辐射传输过程日光诱导叶绿素荧光模拟 前情提要 本文默认您对LESS (LargE-Scale remote sensing data and image Simulation framework) 模型和叶绿素荧光(Sun-Induced chlorophyll Fluorescence, SIF)有…

2023华为杯研究生数学建模研赛E题出血脑卒中完整论文(含28个详细预处理数据及结果表格)

大家好呀&#xff0c;从发布赛题一直到现在&#xff0c;总算完成了全国研究生数学建模竞赛&#xff08;数模研赛&#xff09;E题完整的成品论文。 本论文可以保证原创&#xff0c;保证高质量。绝不是随便引用一大堆模型和代码复制粘贴进来完全没有应用糊弄人的垃圾半成品论文。…

高级运维学习(十)系统安全

kali 实际上它就是一个预安装了很多安全工具的Debian Linux [rootmyhost ~]# kali reset kali reset OK. 该虚拟机系统用户名为:kali,密码为:kali 基础配置 $ ip a s # 查看网络IP地址&#xff0c;本例中查看到的是192.168.88.40 $ sudo systemctl start ssh # 启s…

java面试题-并发编程基础

1.线程的基础知识 1.1 线程和进程的区别&#xff1f; 难易程度&#xff1a;☆☆ 出现频率&#xff1a;☆☆☆ 程序由指令和数据组成&#xff0c;但这些指令要运行&#xff0c;数据要读写&#xff0c;就必须将指令加载至 CPU&#xff0c;数据加载至内存。在指令运行过程中还需要…

驱动开发,基于gpio子系统编写LED灯的驱动,亮灭控制

1.gpio子系统介绍 一个芯片厂商生产出芯片后会给linux提供一个当前芯片中gpio外设的驱动&#xff0c;我们当前只需要调用对应的厂商驱动即可完成硬件的控制。而linux内核源码中的gpio厂商驱动有很多&#xff0c;这里linux内核对厂商驱动做了一些封装&#xff0c;提供了一系列的…

中秋国庆内卷之我爱学习C++

文章目录 前言Ⅰ. 内联函数0x00 内联函数和宏的比较0x01 内联函数的概念0x02 内联函数的特性 Ⅱ. auto&#xff08;C 11)0x00 auto的概念0x01 auto的用途 Ⅲ. 范围for循环(C11)0x00 基本用法0x01 范围for循环(C11)的使用条件 Ⅳ. 指针空值nullptr(C11)0x00 概念 前言 亲爱的夏…

leetcode Top100(17)矩阵置零

给定一个 m x n 的矩阵&#xff0c;如果一个元素为 0 &#xff0c;则将其所在行和列的所有元素都设为 0 。请使用 原地 算法。 示例 1&#xff1a; 输入&#xff1a;matrix [[1,1,1],[1,0,1],[1,1,1]] 输出&#xff1a;[[1,0,1],[0,0,0],[1,0,1]]示例 2&#xff1a; 输入&…

C++ -- 类型转换

目录 C语言中的类型转换 为什么C需要四种类型转换 C 类型转换 static_cast reinterpret_cast const_cast 添加关键字 volatile dynamic_cast 补充 RTTI 总结 C语言中的类型转换 在C语言中&#xff0c;如果赋值运算符左右两侧类型不同&#xff0c;或者形参与实参类型…

如何搜索浏览器添加印象笔记中搜藏的结果

在印象笔记记录的东西多了&#xff0c;就放在哪里不动&#xff0c;失去记录的意义了 1、如何将浏览器中添加印象笔记一块的搜索结果 2、需要两个步骤 第一&#xff1a;将浏览器中添加印象笔记的插件 第二&#xff1a;将印象笔记中的搜索方法勾上&#xff0c;如下 结果如下&…

【从0学习Solidity】41. WETH

【从0学习Solidity】41. WETH 博主简介&#xff1a;不写代码没饭吃&#xff0c;一名全栈领域的创作者&#xff0c;专注于研究互联网产品的解决方案和技术。熟悉云原生、微服务架构&#xff0c;分享一些项目实战经验以及前沿技术的见解。关注我们的主页&#xff0c;探索全栈开发…

反射详细说明

反射概述 反射是指对于任何一个Class类&#xff0c;在"运行的时候"都可以直接得到这个类全部成分。 在运行时,可以直接得到这个类的构造器对象&#xff1a;Constructor。 在运行时,可以直接得到这个类的成员变量对象&#xff1a;Field。 在运行时,可以直接得到这…

【从0学习Solidity】 40. ERC1155

【从0学习Solidity】40. ERC1155 博主简介&#xff1a;不写代码没饭吃&#xff0c;一名全栈领域的创作者&#xff0c;专注于研究互联网产品的解决方案和技术。熟悉云原生、微服务架构&#xff0c;分享一些项目实战经验以及前沿技术的见解。关注我们的主页&#xff0c;探索全栈开…

《动手学深度学习 Pytorch版》 7.1 深度卷积神经网络(AlexNet)

7.1.1 学习表征 深度卷积神经网络的突破出现在2012年。突破可归因于以下两个关键因素&#xff1a; 缺少的成分&#xff1a;数据 数据集紧缺的情况在 2010 年前后兴起的大数据浪潮中得到改善。ImageNet 挑战赛中&#xff0c;ImageNet数据集由斯坦福大学教授李飞飞小组的研究人…