【面试】标准库相关题型(一)

news2024/11/14 13:41:17

文章目录

    • 1. vector底层实现原理
      • 1.1 类构成
      • 1.2 构造函数
      • 1.3 插入元素
      • 1.4 删除元素
      • 1.5 读取元素
      • 1.6 修改元素
      • 1.7 释放空间
    • 2. vector内存增长机制
      • 2.1 特点
      • 2.2 内存增长特性
      • 2.3 内存增长过程
      • 2.4 内存清理
      • 2.5 注意事项
    • 3. vector中reserve和resize的区别
      • 3.1 共同点
      • 3.2 区别
      • 3.3 应用场景
    • 4. vector的元素类型为什么不能是引用?
      • 4.1 引用有什么特征?
      • 4.2 Vector的元素类型不能是引用
    • 5. list底层实现原理
      • 5.1 原理图
      • 5.2 类构成
      • 5.3 构造函数
      • 5.4 迭代器
      • 5.5 访问元素
      • 5.6 插入和删除元素

1. vector底层实现原理

一句话概括:底层实现了一个动态数组

1.1 类构成

  • class vector : protected Vector base
    • protected继承:基类的 public 在子类中将变为 protected,其他权限不改变
    • 它实现了一个动态的数组,内部包含指向动态分配内存的指针以及记录数组大小和容量的成员变量
  • _Vector_base类的数据成员
    • _M_start:指向容器开始的位置
    • _M_finish:指向容器的下一个可插入位置
    • _M_end_of_storage:指向分配的动态内存的尾部

1.2 构造函数

  • 无参构造
    • 不立即分配内存,而是在添加元素时按需分配,避免无谓的内存浪费。
    • STL容器总是性能优先
  • 初始化元素个数构造
    • 根据传入的元素数量分配内存,避免后续可能的重新分配
    • 避免多次申请动态内存,从而影响性能
    • 如果知道需要多大的空间,使用这种方法能大大减少动态分配内存的开销

1.3 插入元素

  • 插入最后
    • 如果有足够的内存空间,则直接插入。如果内存空间不足,则将现有元素复制到新的更大的内存区域,释放原来的内存,然后插入新元素
  • 插入不是最后
    • 对于非尾部插入,除了要分配可能的新内存之外,还需要将插入点之后的所有元素向后移动一位

1.4 删除元素

  • 删除最后一个元素
    • 简单地将_M_finish向前移动一位,不立即释放内存,这样可以为后续的插入操作保留空间
  • 删除不是最后一个元素
    • 待删位置之后元素向前平移一位,_M_finish向前移动一位
    • 删除元素同样不会释放现有已经申请的内存

1.5 读取元素

  • 操作符 []:不检查下标的合法性,所以它的效率比 at()
  • at() 函数:在返回元素之前检查下标的合法性,如果下标越界,会抛出 std::out_of_range 异常
  • 它们都是返回具体元素的引用

1.6 修改元素

  • vector 不支持直接修改某个位置的元素
  • 可以通过获取元素的引用,然后直接修改引用的值,来实现对元素的修改

1.7 释放空间

  • std::vector::shrink_to_fit() 函数:尝试减小容器的容量以适应其大小,以释放未使用的内存(C++11新特性)
  • 使用交换技巧:创建一个临时的空vector,并与原vector进行交换,从而释放其占用的全部内存

2. vector内存增长机制

2.1 特点

  • 内存空间只会增加不会减少:在进行插入操作时,如果当前内存不足以容纳更多的元素,std::vector将申请更大的内存空间,但是在删除元素时,它并不会释放已经申请的内存空间
  • vector的内存是连续的:这意味着可以通过指针算术来遍历vector的元素
  • 不同平台或库实现,内存增长方式可能不同:GCC的实现通常会选择翻倍的方式进行增长,而Visual Studio的实现可能选择1.5倍的方式进行增长

2.2 内存增长特性

  • 无参构造,连续插入一个元素,内存增长方式:1、2、4、8、16、32、…,即每次都是上一次的2倍
  • 有参构造,连续插入一个元素,内存增长方式:n、2n、4n、…,其中n是初始时分配的元素数量

2.3 内存增长过程

  • 申请新的更大的内存空间,大小通常是当前内存空间大小的两倍(具体取决于实现)
  • 将原有内存空间中的数据移动(或复制)到新的内存空间中
  • 释放原有内存空间
  • 在新内存空间的尾部插入新的元素

2.4 内存清理

  • 交换一个空的vector:通过创建一个临时的空vector,并与原vector进行交换,可以释放原vector占用的全部内存:

    std::vector<int> v;
    // ... 后续操作
    std::vector<int>().swap(v);  // 释放内存
    
  • 使用 std::vector::shrink_to_fit 方法:尝试减小容器的容量以适应其大小,以释放未使用的内存:

    std::vector<int> v;
    // ... 后续操作
    v.shrink_to_fit();  // 释放未使用的内存
    

2.5 注意事项

  • std::vector中的元素是指针时,std::vector在销毁时不会调用指针指向的对象的析构函数。所以如果std::vector存储的是动态分配的对象,你需要在std::vector被销毁前,自己手动删除这些对象,以防止内存泄漏:

    std::vector<int*> v;
    for (int i = 0; i < 10; ++i) {
        v.push_back(new int(i));
    }
    
    // 手动释放内存
    for (auto ptr : v) {
        delete ptr;
    }
    

3. vector中reserve和resize的区别

3.1 共同点

  • 对容器内原有的元素不产生影响:无论是reserve还是resize,它们都不会影响容器中已经存在的元素
  • 只能增加容器的容量:如果指定的值小于当前的容量,reserveresize都不会减少容器的容量

3.2 区别

  • reserve:只会改变std::vector的容量(capacity),并不会改变其大小(size)。也就是说,它只会预分配内存,但并不会创建新的元素。这意味着,在调用reserve之后,std::vectorsize成员函数返回的值是不会改变的:

    std::vector<int> v;
    v.reserve(100);  // v的容量变为100,但size仍然为0
    
  • resize:会改变std::vector的大小,同时也可能改变其容量。resize会创建新的元素,所以在调用resize之后,std::vectorsize成员函数返回的值可能会改变:

    std::vector<int> v;
    v.resize(100);  // v的容量和size都变为100
    

3.3 应用场景

  • reserve:在已知需要存储大量元素的情况下使用,可以通过一次性分配足够的内存来避免频繁的内存重新分配,从而提高性能
  • resize:确保容器中有足够的元素,这对于使用下标访问元素的操作是必要的,可以避免越界的问题

4. vector的元素类型为什么不能是引用?

std::vector的模板参数表示容器中存储的元素类型,例如std::vector<int>表示一个整数的动态数组。当尝试定义std::vector<T&>(T为任意类型)时,编译器会报错。原因如下:

4.1 引用有什么特征?

  • 引用必须在定义时进行初始化,不能初始化为空对象,且初始化后不能改变引用的指向。

    int a = 10;
    int& ref = a;  // 正确
    int& ref2;  // 错误,引用必须在定义时初始化
    ref = 20;  // 正确,改变的是a的值,而非ref的指向
    int b = 30;
    ref = b;  // 错误,不能改变ref的指向
    
  • 引用是别名,不是对象,没有实际的地址,不能定义引用的指针,也不能定义引用的引用。

    int a = 10;
    int& ref = a;
    int&* p = &ref;  // 错误,不能定义引用的指针
    int&& ref2 = ref;  // 错误,不能定义引用的引用
    

4.2 Vector的元素类型不能是引用

  • std::vector 在内部使用内存分配操作为元素分配存储空间,但引用不是对象,没有实际的地址,因此不能为其分配存储空间。
  • std::vector::push_backstd::vector::emplace_back 都会尝试复制或移动其参数,以创建新的元素。然而,引用不能被赋值,只能在定义时进行初始化。
  • std::vector 的有参构造函数会尝试初始化一定数量的元素,但引用必须在定义时初始化,因此不能用于初始化 std::vector 的元素。
  • 基于操作符 []at,将会获取引用的引用,这在 C++ 中是不合法的,从而产生矛盾。

5. list底层实现原理

一句话概括:list底层实现了一个双向循环链表

5.1 原理图

素材来源于网络,侵删

5.2 类构成

  • class list : protected _List_base
    • liststd::list 的主体,提供接口和基本功能
  • _list_base._list_impl._list_node
    • _list_node 是链表中的节点,包含了节点中存储的数据以及指向其他节点的指针。
      • _M_storage:存储具体的值。
      • _M_next:指向下一个元素。
      • _M_prev:指向上一个元素。

5.3 构造函数

  • 无论如何构造,std::list 都会初始化一个空节点,这个空节点用来标记链表的终点,它没有存储任何用户数据

5.4 迭代器

std::list 提供了双向迭代器,通过这些迭代器,可以对链表进行前向和后向的遍历

  • ++:将迭代器向后移动到下一个节点
  • --:将迭代器向前移动到前一个节点

5.5 访问元素

  • 获取第一个元素:空节点的下一个节点存储了链表的第一个元素,可以通过 begin() 方法获取
  • 获取最后一个元素:空节点的上一个节点存储了链表的最后一个元素,可以通过 --end() 获取

5.6 插入和删除元素

  • 插入元素:std::list 对于每个新插入的元素,都会创建一个新的节点并动态为其分配内存。插入操作包括 push_back, push_front, insert 等方法
  • 删除元素:std::list 删除元素时会释放对应节点的内存。删除操作包括 pop_back, pop_front, erase 等方法

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

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

相关文章

LangChain入门介绍

原文首发于博客文章LangChain介绍 我们先看看官方的定义 LangChain是一个基于语言模型开发应用程序的框架。它可以实现以下应用程序&#xff1a; 数据感知&#xff1a;将语言模型连接到其他数据源自主性&#xff1a;允许语言模型与其环境进行交互 LangChain的主要价值在于&…

现在可以使用开发者工具为苹果Vision Pro创建空间体验

库比蒂诺&#xff0c;加利福尼亚—苹果公司今天宣布&#xff0c;全新的软件工具及技术现已可供开发者使用&#xff0c;它们能够用于为苹果首款空间计算机—Apple Vision Pro&#xff0c;创造出独特且前所未有的应用体验。Vision Pro具备visionOS&#xff0c;这是全球首款空间操…

【ABAP】数据类型(三)「数据字典数据类型」

&#x1f482;作者简介&#xff1a; THUNDER王&#xff0c;一名热爱财税和SAP ABAP编程以及热爱分享的博主。目前于江西师范大学本科在读&#xff0c;同时任汉硕云&#xff08;广东&#xff09;科技有限公司ABAP开发顾问。在学习工作中&#xff0c;我通常使用偏后端的开发语言A…

Unity简单的移动相机

Unity3D制作一个会移动的方块&#xff08;还不会移动照相机&#xff09;_SMG_DSG的博客-CSDN博客 接着上一次的文章代码&#xff0c;我们继续写&#xff0c;其实简单的移动也是非常简单&#xff0c;我们只需要使用一个相机一直面对着方块的函数就行了 好了&#xff0c;废话不…

Tkinter之窗口布局介绍

Tkinter之窗口布局介绍 关于Python 的Tkinter窗口基础可参见https://blog.csdn.net/cnds123/article/details/127227651 Tkinter 之几何管理器&#xff08;geometry manager&#xff09;&#xff0c;也叫布局&#xff08;layout&#xff09;&#xff0c;是用来控制窗体中小部…

STM32单片机(八)DMA直接存储器存取----第二节:DMA直接存储器存取练习(DMA数据转运和DMA+AD多通道)

❤️ 专栏简介&#xff1a;本专栏记录了从零学习单片机的过程&#xff0c;其中包括51单片机和STM32单片机两部分&#xff1b;建议先学习51单片机&#xff0c;其是STM32等高级单片机的基础&#xff1b;这样再学习STM32时才能融会贯通。 ☀️ 专栏适用人群 &#xff1a;适用于想要…

PyTorch深度学习实战(4)——常用激活函数和损失函数详解

PyTorch深度学习实战&#xff08;4&#xff09;——常用激活函数和损失函数详解 0. 前言1. 常用激活函数1.1 Sigmoid 激活函数1.2 Tanh 激活函数1.3 ReLU 激活函数1.4 线性激活函数1.5 Softmax 激活函数 2. 常用损失函数2.1 均方误差2.2 平均绝对误差2.3 分类交叉熵 2.4 实现自…

分享一组开关按钮

先看效果&#xff1a; 再看代码&#xff1a; <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><title>豆子开关</title><style>* {margin: 0;padding: 0;box-sizing: border-box;-webkit-tap-hi…

STM32单片机(八)DMA直接存储器存取----第一节:DMA直接存储器存取

❤️ 专栏简介&#xff1a;本专栏记录了从零学习单片机的过程&#xff0c;其中包括51单片机和STM32单片机两部分&#xff1b;建议先学习51单片机&#xff0c;其是STM32等高级单片机的基础&#xff1b;这样再学习STM32时才能融会贯通。 ☀️ 专栏适用人群 &#xff1a;适用于想要…

使用Python制作简单的图表并设置图表元素

案例01 在python中制作简单的图表 import matplotlib.pyplot as plt # 导入matplotlib模块 x [1, 2, 3, 4, 5, 6] # 给出x坐标的数据 y [2, 4, 6, 8, 10, 12] # 给出y坐标的数据 plt.plot(x, y, color red, linewidth 3, linestyle solid) # 绘制折线图 plt.show() # …

动态库的入口——VCRT(DLL)和CRT(SO)

摘要&#xff1a;为了更加深入的理解动态库的加载初始化过程&#xff0c;本文根据VCRT和Linux-CRT的代码实现详细描述了windows和linux平台下对应动态库加载时会进行哪些工作。本文重点关注全局变量的初始化时机&#xff0c;以及是否有其他额外的操作。   关键字&#xff1a;…

被微服务循环依赖调用坑了 !

最近的迭代转测后&#xff0c;遇到了一个比较有意思的问题。系统在测试环境整体运行还算平稳&#xff0c;但是过一段时间之后&#xff0c;就开始有接口超时了&#xff0c;日志中出现非常多的 “java.net.SocketTimeoutException: Read timed out”。 试了几次重启大法&#xf…

用魔法打败魔法!用AI制作AI分割数据集!

本节内容我们使用SAM将边界框转换为分割数据集&#xff0c;这对于实例分割数据集的制作非常有用&#xff0c;下面我会一步步给出我的代码&#xff0c;希望对你有用。 有兴趣的朋友可以研究一下这本书&#xff0c;详细的介绍了数据集制作到分割的实际项目应用&#xff01; 步骤 …

【 计算机组成原理 】期末重点

文章目录 前言第一章 【计算机系统概论】1.1 知识点1.1核心例题 第二章 【运算方法和运算器】2.1 知识点2.2 核心例题 第三章 【存储系统】3.1 知识点3.2 核心例题 第四章 【指令系统】4.1 知识点4.2 核心例题 第五章 【中央处理器】5.1 知识点5.2 核心例题 第六章6.1 知识点6.…

【MSP432电机驱动学习—上篇】TB6612带稳压电机驱动模块、MG310电机、霍尔编码器

所用控制板型号&#xff1a;MSP432P401r 今日终于得以继续我的电赛小车速通之路&#xff1a; 苏轼云 “ 素面常嫌粉涴 &#xff0c; 洗妆不褪朱红。 ” 这告诫我们不能只注重在表面粉饰虚伪的自己&#xff0c;要像梅花一样&#xff0c;不断磨砺自己的内在~ 后半句是 “…

JavaSE基础语法--类和对象

在Java中&#xff0c;一切皆为对象&#xff0c;类和对象是一个抽象的概念。我们可以从面向过程来过渡到面向对象。 那么什么是面向过程呢&#xff1f; 举一个简单的例子&#xff0c;现实生活中&#xff0c;你需要买一台手机的时候会经历如下步骤&#xff1a; 这里的每一步都可…

【python程序设计】——期末大作业

【python程序设计】——期末大作业&#x1f60e; 前言&#x1f64c;一、所用技术&#xff1a;二、 系统设计三、 系统实现3.1 核心功能代码实现&#xff1a;3.2 演示结果展示 总结撒花&#x1f49e; &#x1f60e;博客昵称&#xff1a;博客小梦 &#x1f60a;最喜欢的座右铭&am…

yaffs格式的根文件系统制作

linux内核启动后&#xff0c;它接下来要做的事就是启动应用程序&#xff0c;而应用程序在哪里呢&#xff0c;类比windows&#xff0c;启动时要读取c盘&#xff0c;所以linux的文件系统就类似于c盘&#xff0c;并且我们使用的ls、cp等一些类命令&#xff08;本质是应用程序&…

JavaScript 手写代码 第二期

文章目录 1.为什么要手写代码&#xff1f;2. 手写代码2.1 手写实现判断类型函数2.1.1 前置知识2.1.1 手写实现 2.2 手写实现aplly,call,bind方法2.2.1 基本使用2.2.2 实现思路2.2.3 手写实现 1.为什么要手写代码&#xff1f; 我们在日常开发过程中&#xff0c;往往都是取出来直…

Linux(centos7)缺失.bashrc文件登录出现bash-4.2

一、问题描述 最近遇到几次登陆linux&#xff08;centos7.5&#xff09;系统后&#xff0c;虽然在/root用户下&#xff0c;但出现了如下界面&#xff1a; 二、解决思路 使用不同的linux发行版本&#xff0c;&#xff08;比如&#xff1a;IP为*...90,以下简称90&#xff09;会…