C++ 标准模板库(STL)_序列式容器——Vector以及扩容操作(侯捷老师)

2023-05-16

STL—— Vector容器

  • Vector
    • 1、定义
    • 2、数据结构
    • 3、vector成倍扩容过程及部分源码
      • 3.1、扩容条件
      • 3.2、扩容步骤(3步)
      • 3.3、扩容操作部分源码( insert_aux ) ——push_back() + insert()
    • 4、vector基本用法
      • 4.1 vector容器构造函数
      • 4.2 增加函数(push_back + insert)
      • 4.3 删除函数
      • 4.4 其他常用
      • 4.5 vector常用算法
  • 参考

Vector

#include<vector>

1、定义

在这里插入图片描述
vector占用一块连续分配的内存,一种可以存储任意类型的动态数组,与array不同的地方就是:数组是静态分配空间,一旦分配了空间的大小,就不可再改变了;而vector是动态分配空间,随着元素的不断插入,它会按照自身的一套机制不断扩充自身的容量。
属性:

  • 1、支持随机访问
  • 2、末尾相对快速地添加/删除元素的方法(O(1)时间复杂度)
  • 3、动态内存分配(成倍扩容(面试热点))

2、数据结构

在这里插入图片描述

// alloc是SGI STL的空间配置器
template <class T, class Alloc = alloc>
class vector
{
public:
    // vector的嵌套类型定义,typedefs用于提供iterator_traits<I>支持
    typedef T value_type;//数据类型
    typedef value_type* pointer;//指针
    typedef value_type* iterator;
    typedef value_type& reference;//引用
    typedef size_t size_type;
    typedef ptrdiff_t difference_type;
protected:
    // 这个提供STL标准的allocator接口
    typedef simple_alloc <value_type, Alloc> data_allocator;//simple_alloc是SGI STL的空间配置器

    iterator start;               // 表示目前使用空间的头
    iterator finish;              // 表示目前使用空间的尾
    iterator end_of_storage;      // 表示实际分配内存空间的尾
public:
	// 获取几种迭代器
    iterator begin() { return start; }
    iterator end() { return finish; }
    // 返回当前对象个数
    size_type size() const { return size_type(end() - begin()); }
    // 返回重新分配内存前最多能存储的对象个数
    size_type capacity() const { return size_type(end_of_storage - begin()); }
    bool empty() const { return begin() == end(); }
    reference operator[](size_type n) { return *(begin() + n); }
    // 提供访问函数
    reference front() { return *begin(); }
    reference back() { return *(end() - 1); }
...
}

vector数据结构如下,通过三个迭代器start, finish, end_of_storage的系列public接口,可很好地完成数据存储、溢出判断(iter >= iv.end())、大小、容量(容量与大小不等,以免不断申请空间耗费资源)、重载操作符[]、判空、最前元素、最后元素等等。

vector 原始对象本身大小为12(32位)对应3个指针。

  	iterator start;               // 表示目前使用空间的头
    iterator finish;              // 表示目前使用空间的尾
    iterator end_of_storage;      // 表示实际分配内存空间的尾

在VS中可用sizeof(vector)来输出查看。

3、vector成倍扩容过程及部分源码

在这里插入图片描述
在这里插入图片描述
vector是动态空间,随着元素的加入,它的内部机制会自行扩充空间以容纳新元素,vector动态增加大小时,并不是在原空间之后持续新空间(因为无法保证原空间之后尚有可供配置的空间),而是以原大小的两倍另外配置一块较大的空间,然后将原内容拷贝过来,然后才开始在原内容之后构造新元素,并释放原空间。

3.1、扩容条件

size==capacity
 - size:实际所包含的元素个数 
 - capacity:容器的容量,指的是在不分配更多内存的情况下,容器可以保存的最多元素个数

3.2、扩容步骤(3步)

 - 1、完全弃用现有的内存空间,成倍申请更大的内存空间;
 -  2、将旧内存空间中的数据,按原有顺序移动到新的内存空间中; 
 - 3、最后将旧的内存空间释放。

面试热点:
vector 容器在进行扩容后,与其相关的指针、引用以及迭代器可能会失效。
start不再指向相同的地址,扩大空间是新请新的更大的空间,因而不仅是start迭代器,其它指向空间变化前vector的迭代器都将失效

3.3、扩容操作部分源码( insert_aux ) ——push_back() + insert()

  • push_back()
    push_back()将新元素插入于vector尾端时,该函数首先检查是否还有备用空间,如果有就直接在备用空间上构造元素,并调整迭代器finish,使vector增大。如果没有空间则扩充空间(重新配置所需空间、所有元素移动到新空间、释放原空间)。
    在这里插入图片描述
 void push_back(const T& x)
    {
        // 内存满足条件则直接追加元素, 否则需要重新分配内存空间
        if (finish != end_of_storage)
        {
            construct(finish, x);
            ++finish;
        }
        else
            insert_aux(end(), x);
    }
  • push_back()
    在这里插入图片描述
iterator insert(iterator position, const T& x)
    {
        size_type n = position - begin();
        if (finish != end_of_storage && position == end())//尾部插入,等同push_back,并且容量没满
        {
            construct(finish, x);
            ++finish;
        }
        else
            insert_aux(position, x);
        return begin() + n;//返回插入位置的迭代器
    }
  • insert_aux
    在这里插入图片描述
template <class T, class Alloc>
void vector<T, Alloc>::insert_aux(iterator position, const T& x)
{
    if (finish = != end_of_storage)         // 还有备用空间
    {
        construct(finish, *(finish - 1));   // 在备用空间起始处构造一个元素,并以vector最后一个元素值为其初值
        ++finish;                           // 调整finish
        // 把[position,finish - 2]的元素后移一位
        T x_copy = x;
        copy_backward(position, finish - 2, finish - 1);
        *position = x_copy;
    }
    else                                    // 已无备用空间
    {  
        const size_type old_size = size();
        const size_type len = old_size != 0 ? 2 * ols_size : 1;
        // 以上配置原则,如果原大小为0,则配置1(个元素)
        // 如果原大小不为0,则配置原大小的两倍
        // 前半段用来放置源数据,后半段准备用来放置新数据

        iterator new_start = data_allocator::allocate(len); // 实际配置
        iterator new_finish = new_start;
        try {
            // 将原 vector 的内容拷贝到新的 vector
            // uninitialized_copy(InputIterator first, InputIterator last, ForwardIterator result);
            // 迭代器 first 指向输入端的起始位置, 迭代器 last 指向输入端的结束位置(前闭后开区间)
            // 迭代器 result 指向输出端(欲初始化空间)的起始位置
            new_finish = uninitialized_copy(start, position, new_start);
            // 为新元素设定初值x
            construct(new_finish, x);
            // 调整finish
            ++new_finish;
            // 将安插点的原内容也拷贝过来
            new_finish = uninitialized_copy(position, finish, new_finish);
        }
        catch (...) {
            destroy(new_start, new_finish);
            data_allocator::deallocate(new_start, len);
            throw;
        }

        // 析构并释放原 vector
        destory(begin(), end());
        deallocate();

        // 调整迭代器, 指向新 vector
        start = new_start;
        finish = new_finish;
        end_of_storage = new_start + len;
    }
}

// 将[first,last]按倒序赋值给[first - (result-last), last - (result-last)]
// (result-last)大于0时,相当于将元素[first,last]后移(result-last)位
// (result-last)小于0时,相当于将元素[first,last]前移(result-last)位
template<class BidirectionalIterator1, class BidirectionalIterator2>
BidirectionalIterator2 copy_backward ( BidirectionalIterator1 first,
                                       BidirectionalIterator1 last,
                                       BidirectionalIterator2 result )
{
    while (last != first) *(--result) = *(--last);
    return result;
}

4、vector基本用法

参考

4.1 vector容器构造函数

vector():创建一个空vector
vector(int nSize):创建一个vector,元素个数为nSize
vector(int nSize,const t& t):创建一个vector,元素个数为nSize,且值均为t
vector(const vector&):复制构造函数
vector(begin,end):复制[begin,end)区间内另一个数组的元素到vector中

实例:

 //int型vector  
vector<int> vecInt;  
 
//float型vector  
vector<float> vecFloat;  
 
//嵌套vector
vector<vector<int>> vec;
//结构体或类 类型的vector
class A  
{  
    //空类  
};
vector<A> vecA
struct B
{空结构体}
vector<B> vecB
 
//使用构造函数的初始化
//int型vector,包含3个元素  
vector<int> vecIntA(3);  
      
//int型vector,包含3个元素且每个元素都是9  
vector<int> vecIntB(3,9); 
 
//复制vecIntB到vecIntC  
vector<int> vecIntC(vecIntB); 
 
//复制数组中begin-end这段区间上的值到vector中
int iArray[]={2,4,6};  
vector<int> vecIntD(iArray,iArray+3); 

4.2 增加函数(push_back + insert)

void push_back(const T& x):向量尾部增加一个元素X     //使用vector函数添加数
//使用迭代器添加数
iterator insert(iterator it,const T& x):向量中迭代器指向元素前增加一个元素x
iterator insert(iterator it,int n,const T& x):向量中迭代器指向元素前增加n个相同的元素x
iterator insert(iterator it,const_iterator first,const_iterator last):向量中迭代器指向元素前插入另一个相同类型向量的[first,last)间的数据
//push_back在尾部插入数字1
vector<int> vecIntA;
vecIntA.push_back(1);
 
//使用迭代器在第一个位置插入数值888
vector<int> vecIntB;
vector<int>::iterator it;
vecIntB.insert(it=vecIntB.begin(),888);
 
//使用迭代器在第一个位置前插入数值3个888
vector<int> vecIntC;
vector<int>::iterator it;
vecIntC.insert(it=vecIntC.begin(),3,888);
 

4.3 删除函数

iterator erase(iterator it):删除向量中迭代器指向元素
iterator erase(iterator first,iterator last):删除向量中[first,last)中元素
void pop_back():删除向量中最后一个元素
void clear():清空向量中所有元素
//删除最后一个元素  
vecIntA.pop_back();  
 
//删除第四个位置的元素
vector<int> vecIntA;
vecIntA.erase(vecIntA.begin()+4);
 
//删除第2-5个元素  
vecIntA.erase(vecIntA.begin()+1,vecIntA.begin()+5);
 

4.4 其他常用

判断容器是否为空:vec.empty()
传回第一个数据:vec.front();
清空:vec.clear();
向量数量:vec.size()

4.5 vector常用算法

  • 使用reverse将元素翻转:
    将元素翻转(在vector中,如果一个函数中需要两个迭代器,一般后一个都不包含)。
#include<algorithm>,
reverse(vec.begin(),vec.end());
  • 使用sort排序:
#include<algorithm>
sort(vec.begin(),vec.end());(默认是按升序排列,即从小到大)

可以通过重写排序比较函数按照降序比较,如下:

//定义排序比较函数:
bool Comp(const int &a,const int &b)
{
return a>b;
}

调用时:
sort(vec.begin(),vec.end(),Comp),这样就降序排序。

源码参考

参考

1、https://www.cnblogs.com/sooner/p/3273395.html
2、https://blog.csdn.net/wizardtoh/article/details/82532236
3、https://blog.csdn.net/u011028345/article/details/60475566
4、《STL源码剖析》

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

C++ 标准模板库(STL)_序列式容器——Vector以及扩容操作(侯捷老师) 的相关文章

  • STM32串口通讯(接收完成一整个数据帧再将数据发送出去)

    STM32串口通信可以分为查询 xff0c 中断 xff0c DMA三种方式进行通讯 xff0c 本文主要就中断的方式进行讲解 采用中断的方式进行通讯时 xff0c 可以使能接受非空中断 xff08 RXNE xff09 xff0c 当接收
  • 树的先序、中序、后序遍历

    遍历分分先序 中序 后序 先序 xff1a 先访问根结点 左结点 右结点 中序 xff1a 先访问左结点 根结点 右结点 后序 xff1a 先访问左结点 右结点 根结点 先序 xff1a ABC 中序 xff1a BAC 后序 xff1a
  • 调整Arduino STM32的串口缓存大小的方法

    通常Arduino中调整串口缓存大小的方法是修改HardwareSerial h中的常量 其实根本无需修改系统core中的定义值 xff0c 只需要在代码最上方添加以下常量定义 xff0c 抢在HardwareSerial h之前定义缓存大
  • C++入门学习(头文件)

    1 C 43 43 中的头文件 1 1 标准库中的头文件 C 43 43 标准库中的一切内容都被放在名字空间std中 xff08 名字空间中的内容对外是不可见的 xff09 xff0c 但是带来了一个新问题 xff0c 无数现有的C 43
  • 用for循环实现delay延时原理

    void Delay10ms unsigned int c 误差 0us unsigned char a b for c gt 0 c c可以不用初始化 xff0c 因为默认传的参数即为初始化 for b 61 38 b gt 0 b fo
  • 解决ROS中运行gazebo出现process has died的情况

    项目场景 xff1a gazebo 1 process has died pid 397 exit code 255 cmd opt ros melodic lib gazebo ros gzserver e ode worlds empt
  • 使用Ventoy制作U盘启动项

    最近在安装linux镜像的时候遇到了使用UltraISO软件制作U盘启动盘无法使用的情况 下面介绍另外一个软件把U盘制作成启动盘Ventoy xff1a 下载地址 xff1a Ventoy 使用方法 xff1a 1 下载好Ventoy xf
  • git 快速入手

    目录 一 xff1a 初次使用git及github 二 xff1a 将github上下载的代码上传到自己的github仓库里 三 xff1a 使用HTTP上传自己写的项目至github git常用指令汇总 使用需求 xff1a 初次接触gi
  • 线程、进程、并发、cpu、gpu的联系

    1 线程和进程的区别 进程 xff1a 一个在内存中运行的应用程序 每个进程都有自己独立的一块内存空间 xff0c 一个进程可以有多个线程 比如在Windows系统中 xff0c 一个运行的xx exe就是一个进程 线程 xff1a 进程中
  • ubuntu系统安装cuda、cudnn、pytorch和libtorch

    1 安装cuda和cudnn 本机安装的cuda版本11 0 2 cudnn版本 v8 0 5 cu11 0 Ubuntu20 04下CUDA cuDNN的详细安装与配置过程 xff08 图文 xff09 ubuntu20 04安装cuda
  • 深度学习语法篇

    一 基本常识 图像的分辨率的通道数 分辨率和通道数是两个不同的概念 分辨率指的是图像的像素数量 xff0c 它反映了图像的清晰度和细节程度 例如 xff0c 一个分辨率为64x64的图像意味着它有64个像素行和64个像素列 xff0c 总共
  • 第二讲:线性表示及坐标

    第二讲 xff1a 线性表示及坐标 一 线性表示 1 线性表示定义 xff1a 设 是线性空间V中的向量 xff0c 若存在V中一组向量 1 xff0c 2 xff0c xff0c n xff0c 及一组数x1 xff0c x2 xff0c
  • 快速理解掌握指针

    p gt next 61 q 像这种语句 xff0c 表示改变了p后面的连接关系 p 61 q gt next 这类语句 xff0c 没改变连接关系 xff0c 只是赋值而已 解读代码中指针所代表的节点之间的前后连接关系 只要输出该指针对应
  • 第三讲:子空间

    第三讲 xff1a 子空间 一 子空间定义 1 子空间 xff1a 设V是数域F上的线性空间 xff0c W是V的子集 xff0c 若对W中的任意元素 xff0c 及数K F xff0c 按V中的加法和数乘有 xff1a 1 xff09 4
  • Qt多线程之线程之间的传递数据

    hpp span class token macro property span class token directive keyword ifndef span MAINWINDOW H span span class token ma
  • 循环队列c代码实现

    循环队列的抽象数据类型 ADT 队列 xff08 Queue xff09 Data 同线性表 元素具有相同的类型 xff0c 相邻元素具有前驱和后继的关系 Operator span class token function InitQue
  • CMake(四):变量

    前面展示了如何定义基本目标和生成构建输出 就其本身而言 xff0c 这已经很有用了 xff0c 但CMake还附带了一大堆其他特性 xff0c 这些特性带来了极大的灵活性和便利性 本章涵盖了CMake最基本的部分之一 xff0c 即变量的使
  • CMake(六):使用子目录

    对于简单的项目 xff0c 将所有内容保存在一个目录中是可以的 xff0c 但是大多数实际项目倾向于将它们的文件分割到多个目录中 通常可以找到不同的文件类型或分组在各自的目录下的独立模块 xff0c 或者将属于逻辑功能组的文件放在项目目录层
  • CMake(九):生成器表达式

    当运行CMake时 xff0c 开发人员倾向于认为它是一个简单的步骤 xff0c 需要读取项目的CMakeLists txt文件 xff0c 并生成相关的特定于生成器的项目文件集 例如Visual Studio解决方案和项目文件 xff0c
  • CNNs系列---AlexNet网络介绍

    CNNs系列 AlexNet介绍 导言AlexNet介绍1 网络结构 1 参数量 计算量和输出尺寸计算公式 2 网络参数解析 2 AlexNet中涉及到的知识点 1 基本概念 2 AlexNet网络结构的贡献 导言 我们将开启关于卷积神经网

随机推荐

  • CNNS:基于AlexNet的分类任务

    CNNS 基于AlexNet的分类任务 数据集介绍1 pokeman数据集介绍2 flower数据集介绍 超参数对模型的影响1 激活函数对模型的影响 1 使用 96 Sigmoid 96 进行训练 2 使用 96 tanh 96 进行训练
  • CNNs: AlexNet补充

    CNNs AlexNet的补充 导言对 96 AlexNet 96 模型进行调整模型不同层的表征其他探索总结 导言 上上篇和上一篇我们详细地讲述了AlexNet的网络结构和不同超参数对同一数据集的不同实验现象 本节 xff0c 我们就Ale
  • 解决“Permission denied, please try again.”的问题

    在Ubuntu的终端输入命令 ssh highlight highlight是本地主机名称提示输入用户密码 当密码输入正确时 xff0c 仍返回错误 xff1a Permission denied please try again 解决的办
  • leetcode学习常用网站

    C 43 43 网站 cplusplus com map find C 43 43 Reference github com leopeng1995 acplusplus Morris Traversal方法遍历二叉树 xff08 非递归
  • arctan对照表

    注 xff1a 实际调用的是C 43 43 的atan2接口 arctan y x resultstd cout lt lt atan2 0 1 lt lt std endl 0std cout lt lt atan2 0 707 0 70
  • 关于optimized out

    根据网络上的说法 xff0c 调试期间如果一个变量的值显示 optimized out xff0c 那么就表明编译器将该变量进行了优化 xff0c 导致其值不可见 解决的方法是 xff0c 设置编译优化选项 xff0c 禁止相关的优化 可以
  • Ubuntu命令行中重复执行一个程序

    以下示例中 xff0c 执行program 10次 xff0c 并将运行日志以追加的方式重定向到log txt文件中 xff0c progam的入口参数是param for i in 1 10 do program param gt gt
  • 技术知识库

    我对自动控制技术发展趋势的理解 对数学理论的运用越来越深入 xff0c 对计算机的依赖越来越高 xff1b 与人们生产生活的契合越来越紧密以至于无法分割 xff1b 越来越向人类思维的本质倾向 心想事成靠拢 xff0c 让少数人的大脑和肢体
  • [AR论文阅读] Tracking Requirements for Augmented Reality

    论文作者 xff1a RONALD AZUMA年份 xff1a 1993论文主题 xff1a 阐述AR系统对6DoF跟踪性能的技术要求 要点 xff1a 三个核心要求 xff1a 高精度 xff0c 低延迟 xff0c 大范围 跟踪精度指标
  • cv::Mat和std::vector的相互转化

    声明 xff1a 代码来自StackOverFlow xff0c 原文链接 span class hljs keyword using span span class hljs keyword namespace span cv span
  • 构建fabMap过程中可能遇到的错误

    1 When OpenCV2 4 9 is not installed the system has OpenCV2 4 8 pre installed in usr lib x86 64 linux gnu and usr include
  • C++处理Ctrl+C中断信号

    span class hljs preprocessor include lt iostream gt span span class hljs preprocessor include lt csignal gt span span cl
  • Ubuntu获取最高权限(su)的方式

    sudo i span class hljs preprocessor 输入当前账户密码 span span class hljs preprocessor 进入su模式 xff08 root权限 xff09 span
  • 头文件被重复包含的危害及解决办法

    头文件被重复包含的危害 1 简单的理解 xff1a 无非就是头文件里有一行 int a 61 1 包含两次就变成了 int a 61 1 int a 61 1 于是变量重复定义 xff0c 报错 类 xff0c 函数同理 而当你写成 ifn
  • acrobat进行OCR文字识别失败

    OCR文字识别失败是因为pdf有一页图片过于华丽 xff0c 无法识别 xff0c 在adobe acrobat报错的时候 xff0c 瞅准这一页的页码 xff0c 然后跳过这一页 xff0c 继续文字识别其他页就可以了 黑底白字识别也会失
  • STL基础篇(适合初学者快速入门)

    1 STL 是什么 作为一个C 43 43 程序设计者 xff0c STL 是一种不可忽视的技术 Standard Template Library STL xff1a 标准模板库 更准确的说是 C 43 43 程序设计语言标准模板库 ST
  • golang 错误处理

    一 defer package main import 34 fmt 34 34 os 34 34 bufio 34 func tryDefer for i 61 0 i lt 100 i 43 43 defer fmt Println i
  • 平台式惯性导航系统简介(持续更新ing)

    惯性导航系统是利用惯性敏感器件 xff0c 通过基准方向 初始位置等信息来确定运载体位置 姿态和速度的自主式航位推算系统 平台式惯性导航系统是与捷联式惯性导航系统相对应的一种导航方式 目录 前言 一 前备知识 1 惯性导航常用坐标系 2 哥
  • C++ 标准模板库(STL)_iterator—— Traits(侯捷老师)

    iterator Traits Traits1 产生背景2 定义2 1 iterator traits中定义的class iterators2 1 iterator traits中定义的non class iterators 3 内嵌类型声
  • C++ 标准模板库(STL)_序列式容器——Vector以及扩容操作(侯捷老师)

    STL Vector容器 Vector1 定义2 数据结构3 vector成倍扩容过程及部分源码3 1 扩容条件3 2 扩容步骤 xff08 3步 xff09 3 3 扩容操作部分源码 insert aux push back 43 ins