【C/C++】标准库相关题型(一)

news2024/11/20 20:28:58

文章目录

    • 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/653317.html

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

相关文章

在 ZBrush 和 Maya 中创建 Chris Hemsworth 的 3D 肖像

今天瑞云渲染小编给大家带来一篇Marius Prsel分享了 Chris Hemsworth 项目背后的工作过程&#xff0c;详细介绍了角色的头部、面部和头发是如何制作的&#xff0c;并解释了如何在 Arnold 中完成渲染&#xff0c;一起来看看吧&#xff01; 简介 我的名字是Marius Prsel&#xf…

7个理由:从Java8升级到Java17【翻译】

原文地址: 7 Reasons to Migrate from Java 8 to Java 17 释放吧&#xff0c;Java的全部力量。[手动狗头] 简介 从Java8到Java18&#xff0c;Java已经经历了漫长的发展历程&#xff08;Java20非长期维护版本&#xff09;。同时也是从Java 8开始&#xff0c;Java生态系统发生…

【LeetCode热题100】打卡第23天:最小覆盖子集

文章目录 【LeetCode热题100】打卡第23天&#xff1a;最小覆盖&子集⛅前言 最小覆盖&#x1f512;题目&#x1f511;题解 子集&#x1f512;题目&#x1f511;题解 【LeetCode热题100】打卡第23天&#xff1a;最小覆盖&子集 ⛅前言 大家好&#xff0c;我是知识汲取者&…

三种方法将Word文档转换为PDF文件格式

如何将Word文档转换为PDF文件格式呢&#xff1f;大家在传输文件时&#xff0c;很多人喜欢使用PDF文件格式&#xff0c;因为它非常稳定&#xff0c;不会出现格式混乱的问题。但有些人可能不知道如何进行转换&#xff0c;今天我将介绍三种转换方法&#xff0c;让我们一起来学习一…

从0开始,精通Go语言Rest微服务架构和开发

说在前面 现在拿到offer超级难&#xff0c;甚至连面试电话&#xff0c;一个都搞不到。 尼恩的技术社区中&#xff08;50&#xff09;&#xff0c;很多小伙伴凭借 “左手云原生右手大数据”的绝活&#xff0c;拿到了offer&#xff0c;并且是非常优质的offer&#xff0c;据说年…

Pytest教程__Hook钩子函数总结(14)

前言 pytest 的钩子函数有很多&#xff0c;通过钩子函数的学习可以了解到pytest在执行用例的每个阶段做什么事情&#xff0c;也方便后续对pytest二次开发学习。 详细文档可以查看pytest官方文档API Reference — pytest documentation 钩子函数总结 第一部分&#xff1a;set…

5、DuiLib组件结构的初探

文章目录 1、DuiLib组件结构的初探 1、DuiLib组件结构的初探 DuiLib 整体的实现不仅仅有控件&#xff0c;还有窗口消息、XML处理等模块&#xff0c;官方曾经过出的一个结构图如下&#xff1a; 图中还是比较详细的描述了 DuiLib 的整体设计&#xff0c;值得注意的部分是 “窗口…

每日一练 | 华为认证真题练习Day61

1、DHCPv6服务器发送的DHCPv6 ADVERTISE报文目的端口号为&#xff1f; A. 548 B. 547 C. 549 D. 546 2、当DHCPv6客户端收到DHCPv6服务器发送的RA报文中的和O标记位取值为下列哪个数值时&#xff0c;DHCPv6客户端采用DHCPv6有状态自动配置获取IPv6地址和其它配置信息&#…

Android libusb库的使用

Download Data Center Software from Total Phase. 1 Aptiv DABR Aptiv acquired Indian Unwired&#xff0c;Delphi Automotive USB Bridge / Hub&#xff0c;Hub中集成了UDC&#xff0c;upstream UDC连接的主机称为A-Host&#xff0c;downstream UDC被B-Host枚举成Relay devi…

一个床垫的故事

这是学习笔记的第 2460篇文章 这是一个床垫的真实故事&#xff0c;想起来还是蛮感慨的&#xff0c;真是太魔幻了。 起因是我哥搬家&#xff0c;有一个很新的品牌床垫&#xff0c;因为新房子那边买家具已经送了一个床垫了&#xff0c;所以就多出来一个床垫&#xff0c;他打算把…

autogpt的使用,还有出现的问题

AutoGPT简介 AutoGPT是一个实验性开源应用程序&#xff0c;展示了GPT-4语言模型的功能。该程序由GPT-4驱动&#xff0c;将LLM“思想”链接在一起&#xff0c;以自主实现您设定的任何目标。作为GPT-4完全自主运行的首批例子之一&#xff0c;AutoGPT突破了人工智能的极限。 注&a…

Opencv-C++笔记 (6) : opencv-图片和视频操作

文章目录 一、读取函数imread二、图片窗口函数namedWindow三、 图片保存Imwrite和显示函数Imshow四、视频数据的读取五、摄像头直接调用 一、读取函数imread cv::Mat cv::imread(const String & filename,int flagsIMREAD_COLOR)filename&#xff1a;需要读取图像的文件名…

Linux运维监控学习笔记4

Zabbix相关的一些概念&#xff1a; Zabbix用户和用户群组&#xff1a; 用户&#xff1a;Zabbix提供多用户管理&#xff0c;不同的用户可以设置不同的权限&#xff0c;不同的语言和不同的报警方式。 1&#xff09;创建用户&#xff1a;点击“创建用户”按钮&#xff1a; 2&…

NOTA PEG7 Azide,NOTA-七聚乙二醇叠氮,新型双功能整合剂

NOTA PEG7 Azide中NOTA及其衍生物是新型双功能整合剂之一。NOTA及其衍生物具有良好的配位和鳌合能力&#xff0c;可作为过渡金属离子的配体。叠氮化物基团可以参与铜催化的与炔部分的点击化学反应。 聚乙二醇在科研领域运用广泛&#xff0c;聚乙二醇具有良好的水溶性&#xff0…

【FPGA入门】第一篇、Verilog基本语法常识

目录 第一部分、不同的变量类型 1、wire和reg的区别 2、如何对变量进行赋值呢&#xff1f; 3、什么是阻塞&#xff1f;什么是非阻塞&#xff1f; 第二部分、变量位宽的定义 1、各种系统默认情况 2、变量位宽声明方式 3、表明位宽的情况下&#xff0c;赋值方式 4、两个模…

来自一个敲了5年代码的网络安全工程师的自述(目前薪资30K)

本人是一名敲了5年半代码的网络安全工程师&#xff0c;目前在杭州工作&#xff0c;月薪目前是在30.6K左右&#xff0c;经历过两次跳槽&#xff0c;第一次跳槽拿到了12K的offer&#xff0c;第二次跳槽拿到18K的offer。一直到目前为止的30K左右。 说到这里再给大家提个醒&#x…

SpringBoot整合定时任务技术Quartz

个人简介&#xff1a;Java领域新星创作者&#xff1b;阿里云技术博主、星级博主、专家博主&#xff1b;正在Java学习的路上摸爬滚打&#xff0c;记录学习的过程~ 个人主页&#xff1a;.29.的博客 学习社区&#xff1a;进去逛一逛~ RequestMapping注解 &#x1f680;Quartz应用场…

好消息!PMP证书还没过期的宝子,可以增持免考CSPM-2证书

2021年10月&#xff0c;中共中央、国务院发布的《国家标准化发展纲要》明确提出构建多层次从业人员培养培训体系&#xff0c;开展专业人才培养培训和国家质量基础设施综合教育。建立健全人才的职业能力评价和激励机制。由中国标准化协会&#xff08;CAS&#xff09;组织开展的项…

如何使用hexo next主题,新建一个专栏

文章目录 backgroundIntroHow to do that&#xff1f;&#x1f388; background 今天突发奇想&#xff0c;想要在自己的博客中增加一个新的专栏&#xff0c;记录自己的一些随想。起因是不想让博客成为一个纯粹记录技术成长的网站&#xff0c;&#xff08;毕竟如果真的有人要看…

PostgreSQL(八)锁的相关操作

目录 一、锁存在的意义二、表级锁1.ACCESS SHARE2.ROW SHARE3.ROW EXCLUSIVE4.SHARE UPDATE EXCLUSIVE5.SHARE6.SHARE ROW EXCLUSIVE7.EXCLUSIVE8.ACCESS EXCLUSIVE9.表级锁模式冲突表10.示例一11.示例二 三、行级锁1.FOR UPDATE2.FOR NO KEY UPDATE3.FOR SHARE4.FOR KEY SHARE…