【C++】List容器(2)-内存管理-创建-插入-删除-遍历-排序-查找-底层分析-构造和析构函数-内存泄漏等

std::list 是 C++ STL 中的一种双向链表容器,它提供了一系列的迭代器来访问元素,包括前方迭代器、后方迭代器和const迭代器。std::list 采用双向链表实现,每个节点都存储了数据以及两个指向前一个节点和后一个节点的指针。

内存管理

在 C++ 中,std::list 容器是由标准模板库(STL)管理的,这意味着开发者不需要手动管理其内存分配和释放。std::list 容器使用动态内存分配在内部维护一个双向链表,以适应元素的动态插入和删除。

代码举例

下面是一些使用 std::list 的示例代码,包括基本的操作如插入、删除、遍历等。

下面是一些关于std::list容器内存管理的代码示例和说明:

创建 List 容器
#include <iostream>  
#include <list>  int main() {  // 创建一个空的 int 类型的 list 容器  std::list<int> myList;  // 输出 list 的大小  std::cout << "Size of myList: " << myList.size() << std::endl; // 输出: 0  return 0;  
}
插入元素
#include <iostream>  
#include <list>  int main() {  std::list<int> myList;  // 在链表尾部插入元素  myList.push_back(1);  myList.push_back(2);  myList.push_back(3);  // 在链表头部插入元素  myList.push_front(0);  // 在指定位置插入元素  auto it = myList.begin();  std::advance(it, 2); // 将迭代器移动到第3个元素位置  myList.insert(it, 2.5); // 在第3个位置插入一个浮点数(注意:这里会发生类型转换)  // 输出 list 的内容  for (int num : myList) {  std::cout << num << ' ';  }  std::cout << std::endl; // 输出: 0 1 2 2 3  return 0;  
}
#include <iostream>  
#include <list>  int main() {  std::list<int> myList;  // 在链表尾部插入元素  myList.push_back(1); // 分配一个新的节点,并将1存入其中,然后将该节点添加到链表尾部  // 在链表头部插入元素  myList.push_front(0); // 分配一个新的节点,并将0存入其中,然后将该节点添加到链表头部  // 在指定位置插入元素  auto it = myList.begin();  std::advance(it, 1); // 将迭代器移动到第2个元素位置  myList.insert(it, 2); // 分配一个新的节点,并将2存入其中,然后将该节点插入到迭代器it所指向的位置  // 输出 list 的内容  for (const auto& num : myList) {  std::cout << num << ' ';  }  std::cout << std::endl; // 输出: 0 2 1  return 0;  
}
删除元素
#include <iostream>  
#include <list>  int main() {  std::list<int> myList = {0, 1, 2, 3, 4};  // 删除指定值的元素  myList.remove(2); // 删除所有值为2的元素  // 删除指定位置的元素  auto it = myList.begin();  std::advance(it, 2); // 将迭代器移动到第3个元素位置  myList.erase(it); // 删除第3个元素  // 输出 list 的内容  for (int num : myList) {  std::cout << num << ' ';  }  std::cout << std::endl; // 输出: 0 1 3 4  return 0;  
}
#include <iostream>  
#include <list>  int main() {  std::list<int> myList = {0, 1, 2, 3, 4};  // 删除链表头部的元素  myList.pop_front(); // 删除链表头部的节点,并释放其内存  // 删除链表尾部的元素  myList.pop_back(); // 删除链表尾部的节点,并释放其内存  // 删除指定位置的元素  auto it = myList.begin();  std::advance(it, 1); // 将迭代器移动到第2个元素位置  myList.erase(it); // 删除迭代器it所指向的节点,并释放其内存  // 删除所有值为特定值的元素  myList.remove(3); // 遍历链表,删除所有值为3的节点,并释放它们的内存  // 输出 list 的内容  for (const auto& num : myList) {  std::cout << num << ' ';  }  std::cout << std::endl; // 输出: 1 (假设列表中只有一个1)  return 0;  
}
遍历元素
#include <iostream>  
#include <list>  int main() {  std::list<int> myList = {0, 1, 2, 3, 4};  // 使用迭代器遍历 list  for (std::list<int>::iterator it = myList.begin(); it != myList.end(); ++it) {  std::cout << *it << ' ';  }  std::cout << std::endl; // 输出: 0 1 2 3 4  // 使用范围基础的 for循环遍历 list  for (const auto& num : myList) {  std::cout << num << ' ';  }  std::cout << std::endl; // 输出: 0 1 2 3 4  return 0;  
}
排序和查找
#include <iostream>  
#include <list>  
#include <algorithm>  int main() {  std::list<int> myList = {4, 2, 5, 1, 3};  // 对 list 进行排序  myList.sort();  // 查找元素  auto it = std::find(myList.begin(), myList.end(), 3);  if (it != myList.end()) {  std::cout << "Found " << *it << std::endl; // 输出: Found 3  } else {  std::cout << "Not found" << std::endl;  }  return 0;  
}

在以上示例中,std::list 自动管理其内部节点的内存分配和释放。当元素被插入时,新的节点会被创建;当元素被删除时,相应的节点会被释放。
当然可以,下面我将提供更多关于std::list容器的使用示例,并详细解释每个操作对内存管理的影响。

合并两个 List
#include <iostream>  
#include <list>  int main() {  std::list<int> list1 = {1, 2, 3};  std::list<int> list2 = {4, 5, 6};  // 合并两个 list,将 list2 的所有元素移动到 list1 的尾部  list1.splice(list1.end(), list2);  // 输出合并后的 list1 的内容  for (const auto& num : list1) {  std::cout << num << ' ';  }  std::cout << std::endl; // 输出: 1 2 3 4 5 6  // 注意:splice 操作并不会释放 list2 中节点的内存,而是将它们移动到 list1 中  // 因此,操作后 list2 为空,但其内存仍然被分配着  return 0;  
}
拷贝和赋值
#include <iostream>  
#include <list>  int main() {  std::list<int> list1 = {1, 2, 3};  // 拷贝构造一个新的 list  std::list<int> list2(list1); // 分配新的节点,并将 list1 中的元素复制到这些新节点中  // 赋值操作  std::list<int> list3;  list3 = list1; // 分配新的节点(如果 list3 中原有节点,则先释放它们),并将 list1 中的元素复制到这些新节点中  // 输出 list2 和 list3 的内容  for (const auto& num : list2) {  std::cout << num << ' ';  }  std::cout << std::endl; // 输出: 1 2 3  for (const auto& num : list3) {  std::cout << num << ' ';  }  std::cout << std::endl; // 输出: 1 2 3  return 0;  
}

底层分析

std::list 是 C++ STL 中的一种容器,它使用动态内存分配在内部维护一个双向链表。下面是对其底层内存管理的更深入的讲解。

数据结构

std::list 的底层是由节点组成的双向链表。每个节点包含以下几个部分:

  1. 数据部分:存储容器的元素。
  2. 向前指针:指向链表中前一个节点的指针。
  3. 向后指针:指向链表中后一个节点的指针。
    这种数据结构使得 std::list 可以在常数时间内完成插入和删除操作(平均情况),因为这些操作只需要改变节点的指针。
内存分配

当创建 std::list 容器时,STL 会根据需要动态分配内存来创建链表的节点。这些节点会一直保留在内存中,直到容器被销毁。

插入操作

std::list 中插入元素时,STL 会执行以下步骤:

  1. 分配一个新的节点。
  2. 将新节点的数据部分设置为要插入的元素。
  3. 如果插入到链表尾部,则将新节点的向前指针设置为当前尾节点,并将当前尾节点的向后指针设置为新节点。如果插入到链表头部,则将新节点的向后指针设置为当前头节点,并将当前头节点的向前指针设置为新节点。
  4. 如果插入点在中间,则需要更新前一个节点的向后指针和后一个节点的向前指针,使它们都指向新节点。
删除操作

std::list 中删除元素时,STL 会执行以下步骤:

  1. 找到要删除的节点。
  2. 更新前一个节点的向后指针,使其指向要删除节点的后继节点。
  3. 更新后一个节点的向前指针,使其指向要删除节点的前驱节点。
  4. 释放要删除的节点所占用的内存。
内存释放

std::list 容器被销毁时,STL 会自动释放所有节点的内存。这是通过 std::list 的析构函数来完成的,它遍历整个链表,并逐个释放每个节点的内存。

std::list是一个双向链表,它通过动态分配内存来存储元素,每个元素都存储在一个单独的节点中。

构造函数和析构函数

std::list的构造函数负责分配内存来存储元素,而析构函数则负责释放这些内存。

#include <iostream>  
#include <list>  int main() {  // 构造函数分配内存  std::list<int> myList = {1, 2, 3};  // ... 进行一些操作 ...  // 析构函数释放内存  // 当myList离开作用域时,它的析构函数将被调用,自动释放所有节点的内存  
} // myList的析构函数在这里被调用
内存泄漏

由于std::list自动管理其内存,因此,在正常使用情况下,你不会遇到内存泄漏问题。但是,如果你在使用迭代器或指针时不够小心,可能会间接导致内存泄漏。

#include
#include

int main() {
std::list<int*> myList; // 列表存储int指针

int* ptr = new int(5); // 动态分配内存  
myList.push_back(ptr); // 将指针添加到列表中  // ... 进行一些操作 ...  // 忘记释放动态分配的内存将导致内存泄漏  
// 正确的做法是在删除指针之前先释放它指向的内存  
delete ptr; // 释放内存  
ptr = nullptr; // 将指针置为空,避免悬挂指针  // 现在可以安全地从列表中删除指针  
myList.remove(ptr);  return 0;  

}
在这个例子中,我们创建了一个存储int*的std::list。当我们向列表中添加一个动态分配的int的指针时,必须确保在不再需要该int时释放其内存。否则,就会发生内存泄漏。

交换和移动语义
C++11引入了移动语义,它允许在不复制数据的情况下转移资源所有权。std::list支持移动操作,这可以提高效率并减少不必要的内存分配和释放。

#include <iostream>  
#include <list>  int main() {  std::list<int> list1 = {1, 2, 3};  std::list<int> list2;  // 移动赋值操作  list2 = std::move(list1); // list1的资源被转移到list2,list1变为空列表  // 输出 list2 的内容  for (const auto& num : list2) {  std::cout << num << ' ';  }  std::cout << std::endl; // 输出: 1 2 3  // list1现在是空的  return 0;  
}

在移动赋值操作中,list1中的资源(即节点)被转移到list2中,而list1则变为一个空列表,不再拥有任何元素。这避免了不必要的复制操作,提高了性能。

容量和大小
你可以使用std::list的成员函数来检查其容量和大小。

#include <iostream>  
#include <list>  int main() {  std::list<int> myList = {1, 2, 3};  // 获取列表的大小  std::size_t size = myList.size();  std::cout << "Size of myList: " << size << std::endl; // 输出: Size of myList: 3  // 获取列表的最大可能大小(对于list来说,这个值通常没有意义,因为list可以动态增长)  std::size_t max_size = myList.max_size();  std::cout << "Max size of myList: " << max_size << std::endl; // 输出一个很大的数,表示理论上的最大容量  return 0;  
}

这些操作并不直接影响std::list的内存管理,但它们提供了关于容器当前状态的有用信息。

综上所述,std::list通过其内部机制自动管理节点的内存分配和释放,用户通常不需要直接干预这个过程。然而,理解这些机制有助于避免常见的内存管理错误,如内存泄漏和悬挂指针。在使用std::list时,应始终确保遵循良好的编程实践,如避免不必要的动态内存分配和正确管理指针。

注意事项
  • 由于 std::list 使用动态内存分配,因此在容器生命周期内,内存分配和释放操作是自动完成的,不需要手动管理。
  • 在进行插入和删除操作时,需要注意迭代器的失效问题。特别是在执行删除操作后,指向被删除节点的迭代器、指针和引用都会失效。
  • std::list 容器不支持随机访问,因为它不是基于数组的容器。访问元素需要使用迭代器进行顺序遍历。

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

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

相关文章

MATLAB设置变量

您可以通过简单的方式分配变量。例如&#xff0c; 示例 x 3 %定义x并用值初始化它 MATLAB将执行上述语句并返回以下结果- x 3 它创建一个名为x的1乘1矩阵&#xff0c;并将值3存储在其元素中。再举一个实例&#xff0c; 示例 x sqrt(16) %定义x并用表达式初始化它 MATLAB将…

自动化测试Selenium(3)

目录 WebDriver相关API 打印信息 打印title 打印url 浏览器的操作 浏览器最大化 设置浏览器的宽,高 操作浏览器的前进, 后退, 刷新 控制浏览器滚动条 键盘事件 键盘单键用法 键盘组合按键用法 鼠标事件 WebDriver相关API 打印信息 打印title 即打印该网址的标题.…

在PostgreSQL中,如何创建一个触发器并在特定事件发生时执行自定义操作?

文章目录 解决方案示例代码1. 创建自定义函数2. 创建触发器 解释 在PostgreSQL中&#xff0c;触发器&#xff08;trigger&#xff09;是一种数据库对象&#xff0c;它能在特定的事件&#xff08;如INSERT、UPDATE或DELETE&#xff09;发生时自动执行一系列的操作。这些操作可以…

css-Echarts图表初始显示异常非完全显示

1.echarts图表初始加载异常 2.问题原因 初次加载时&#xff0c;由于外层使用%比 echarts dom元素没有完全加载完成&#xff0c;canvas绘画继承本身宽高&#xff0c;造成Echarts图表初始显示异常非完全显示。 3.使用echarts图表可参考以下代码&#xff08;实现一定的自适应&am…

* 玩转数据魔方Plotly Express实战8例

大家好&#xff01;今天我们要一起探索Plotly Express这个超级棒的数据可视化神器。想象一下&#xff0c;你的数据故事能以炫酷图表的形式跃然纸上&#xff0c;是不是很兴奋&#xff1f;Plotly Express就像魔法棒&#xff0c;让复杂数据瞬间变得生动起来。接下来&#xff0c;让…

mac qt android开发环境

1,安装Android Studio 下载 Android Studio 和应用工具 - Android 开发者 | Android Developers (google.cn)

MyBatis 核心配置讲解(上)

大家好&#xff0c;我是王有志&#xff0c;一个分享硬核 Java 技术的互金摸鱼侠。 前两篇的文章中我们分别介绍了 MyBatis 和 MyBaits 的应用组成&#xff0c;到这里基础篇的内容就结束了。 从今天开始&#xff0c;我们正式进入 MyBatis 学习的第二阶段&#xff1a;MyBatis 的…

Linux网络实战(一)- DNS配置

Linux网络实战&#xff08;一&#xff09;- DNS配置 1 Linux 本机DNS配置编程要求预期输出输入 2 安装DNS服务软件编程要求测试说明输入 3 DNS服务器启动/关闭编程要求测试说明输入 4 DNS服务器配置编程要求测试说明输入 1 Linux 本机DNS配置 编程要求 在右侧命令行中配置本地…

【Qt 学习笔记】Qt常用控件 | 显示类控件LCD Number的使用及说明

博客主页&#xff1a;Duck Bro 博客主页系列专栏&#xff1a;Qt 专栏关注博主&#xff0c;后期持续更新系列文章如果有错误感谢请大家批评指出&#xff0c;及时修改感谢大家点赞&#x1f44d;收藏⭐评论✍ Qt常用控件 | 显示类控件LCD Number的使用及说明 文章编号&#xff1a…

“华为杯“华南理工大学程序设计竞赛(同步赛)

目录 最长回文子串 又一道好题 信号塔 网格树 异色边2 M 最长回文子串 构造马拉车 #define int long long//__int128 2^127-1(GCC) #define PII pair<int,int> //#define f first //#define s second const int inf 0x3f3f3f3f3f3f3f3f, N 1e5 5, mod 1e9 7;…

2024年第十五届蓝桥杯省赛C++B组(浙江省第二次,4月21号)【真题解析】

等C语言网或者别的地方更新题目&#xff0c;蓝桥杯真题 - 编程题库 - C语言网 (dotcpp.com)&#xff0c;后序再做更新 题目没带出来有点忘了&#xff0c;下面是不按顺序来的 第一题&#xff1a;一个数被表达成别的数&#xff0c;是否会出现字母。 除进制取余&#xff0c;进制…

stm32中的中断优先级

在工作中使用到多个定时器中断,由于中断的中断优先级不熟悉导致出错,下面来写一下中断的一些注意事项。 一、中断的分类 1、EXTI外部中断:由外部设备或外部信号引发,例如按键按下、外部传感器信号变化等。外部中断用于响应外部事件,并及时处理相关任务。 2、内部中断:…

Matlab对多个输入信号进行数值排序提取特定值

1、将多个信号转为一个数组信号输出&#xff0c;在这里需要注意&#xff0c;数据类型是否统一&#xff1b; 2、使用Sort模块&#xff0c;进行排序&#xff08;可设置排序方向&#xff09;&#xff0c;得到排序后的新数组以及对应的索引号&#xff1b; 3、设置想要的索引号&…

常见算法(二分,分块查找,插入,快速排序)

常见算法 1. 顺序查找 package com.mohuanan.exercise;import java.util.ArrayList;public class BasicFind01 {public static void main(String[] args) {int[] arr {1, 2, 3, 1, 2, 3, 4, 5, 6};int number 2;ArrayList<Integer> indexList findIndex(arr, number…

12.事件参数

事件参数 事件参数可以获取event对象和通过事件传递数据 获取event对象 <template><button click"addCount">Add</button><p>Count is: {{ count }}</p> </template> <script> export default {data() {return {count:0…

6.SpringBoot 日志文件

文章目录 1.日志概述2.日志作用3.使用和观察日志3.1如何观察日志3.2使用日志3.3日志级别3.4日志持久化3.5日志分割 4.日志框架4.1门面模式(外观模式)4.2 SLF4J框架介绍4.3 日志格式的说明4.3.1日志名称 5.日志颜色设置6.总结 大家好&#xff0c;我是晓星航。今天为大家带来的是…

Mac下 allure的下载与配置

前提 电脑安装了jdk1.8 链接&#xff1a;mac下配置JDK环境 下载Allure 1.Allure下载最新版本&#xff1a;Releases allure-framework/allure2 GitHub 2.下载后直接解压就好啦 配置 1.根据电脑终端类型输入对应命令&#xff1a; # bash终端 vim ~/.bash_profile # zsh终…

SpringMvc的核心组件和执行流程

一、 springmvc的核心组件及作用 1.DispatcherServlet:前置控制器&#xff0c;是整个流程控制的核心&#xff0c;用来控制其他组件的执行&#xff0c;降低了其他组件的耦合性 2.Handler:控制器&#xff0c;完成具体的业务逻辑&#xff0c;当DispatcherServlet接收到请求后&am…

Pages by User Role for WordPress:强化内容访问控制的必备插件

在数字化时代&#xff0c;WordPress已成为众多网站开发者和设计师的首选平台。然而&#xff0c;如何根据用户角色精确控制内容的访问权限&#xff0c;一直是困扰他们的难题。Pages by User Role for WordPress插件应运而生&#xff0c;为这一难题提供了完美的解决方案。 Pages …

力扣HOT100 - 23. 合并K个升序链表

解题思路&#xff1a; 只要会合并两个升序链表&#xff0c;合并K个做法类似。 class Solution {public ListNode mergeKLists(ListNode[] lists) {ListNode res null;for (int i 0; i < lists.length; i) {res merge(res, lists[i]);}return res;}public ListNode merg…