C++10(list)

【C++打怪之路Lv10】-- list C++STL - 【list】 阅读详情

作为 C++ 初学者,在接触 STL 容器时,最容易混淆的就是vector和list—— 明明都是存数据的容器,为啥用法和场景差这么多?这篇文章就像你的 “list 专属学习笔记”,用新手能看懂的语言,把 list 的底层逻辑、核心用法、避技巧讲透。

📚 目录(新手友好版)
1.【唠唠基础】list 是个啥?和 vector 差在哪?
2.【核心技能】list 的 “常用招式”(构造 / 迭代器 / 增删改查)
3.【踩坑预警】迭代器失效?新手最容易栽的坑
4.【动手实操】新手也能看懂的 list 模拟实现
5.【终极对比】list vs vector:啥时候用谁?
6.【复习小结】核心知识点一键回顾

1. 【唠唠基础】list 是个啥?和 vector 差在哪?
对于刚学 C++ 的同学来说,先不用死记 “双向循环链表” 这种专业术语,咱们先打个比方:

  • vector就像你手里的一排连续座位,每个座位紧挨着,想找第 5 个座位直接走过去就行(随机访问),但如果要在中间加个座位,就得把后面的人全挪开(插入效率低);
  • list就像一串手拉手的小朋友,每个小朋友都记着前面和后面是谁,想在中间加个小朋友,只需要让前后的小朋友换个牵手对象就行(插入效率高),但想找第 5 个小朋友,得从第一个开始数(不支持随机访问)。

1.1 list 的官方定义(简化版)
list 是 STL 里的序列式容器,底层是「带头结点的双向循环链表」,核心特点:
✅ 优点:任意位置插入 / 删除元素超快(只改指针,不用搬移数据);插入元素不会让迭代器失效;
❌ 缺点:不能直接访问第 n 个元素(比如list[5]会报错);每个节点要存额外的指针,占内存;

1.2 新手必懂的基础对比(先记结论)
在这里插入图片描述
在 C++ 标准库中,std::list 和 std::vector 对于插入操作时的迭代器失效行为不同,根源在于它们的底层存储结构:

vector 内部使用连续内存(类似动态数组)。插入元素时,若容量不足则重新分配整块内存,并将所有元素移动/拷贝到新空间,此时所有迭代器、引用、指针都会失效;即使容量充足,插入点之后的所有元素也会向后移动,导致指向插入点之后元素的迭代器失效。

list 内部使用双向链表。插入新元素只需创建新节点并调整相邻节点的前后指针,原有节点的内存地址不变,因此所有指向已存在元素的迭代器、引用、指针均保持有效(仅指向被插入元素自身的迭代器是新产生的,但不会影响已有的)。

2. 【核心技能】list 的 “常用招式”(构造 / 迭代器 / 增删改查)
新手学 list,先掌握 “最常用的接口” 就行,不用记所有函数,以下是高频使用的核心操作。

2.1 第一步:创建 list(构造函数)
就像买新本子,先选 “空本子”“有 n 页的本子” 还是 “抄别人的本子”,list 有 4 种常用创建方式:

#include <iostream>
#include <list> // 新手必记:用list必须加这个头文件
using namespace std;

int main() {
    // 招式1:创建空list(最常用)
    list<int> l1; 

    // 招式2:创建有5个元素,每个元素都是10的list
    list<int> l2(5, 10); 

    // 招式3:拷贝别人的list(复制l2)
    list<int> l3(l2); 

    // 招式4:用数组初始化list(新手也常用)
    int arr[] = {1,2,3,4,5};
    list<int> l4(arr, arr+5); 

    // 打印l4看看效果(新手小技巧:用迭代器遍历)
    for (auto it = l4.begin(); it != l4.end(); it++) {
        cout << *it << " "; // 输出:1 2 3 4 5
    }
    return 0;
}

2.2 第二步:遍历 list(迭代器的用法)

新手容易懵 “迭代器到底是个啥?”—— 你可以把它理解成 “指向 list 节点的小手”,能帮你一个个摸遍 list 里的元素。
list 支持两种迭代器,新手先掌握正向迭代器就行:

int main() {
    list<int> l = {10,20,30,40,50};

    // 正向迭代器:从第一个元素走到最后一个
    cout << "正向遍历:";
    for (list<int>::iterator it = l.begin(); it != l.end(); it++) {
        cout << *it << " "; // 输出:10 20 30 40 50
    }

    // 反向迭代器(新手了解即可):从最后一个走到第一个
    cout << "\n反向遍历:";
    for (list<int>::reverse_iterator it = l.rbegin(); it != l.rend(); it++) {
        cout << *it << " "; // 输出:50 40 30 20 10
    }
    return 0;
}

💡 小提醒:

  • begin()是 “第一个元素的小手”,end()是 “最后一个元素的下一个位置”(不是最后一个元素!);
  • 反向迭代器的++,其实是往 “前” 走(和正向相反),不用死记,用的时候试一次就懂了。

2.3 第三步:查容量、取元素(高频操作)
list 的容量接口很简单,只有 2 个,元素访问也只有 2 个(因为不能随机访问):

int main() {
    list<int> l = {1,2,3};

    // 1. 查容量:empty(是否为空)、size(元素个数)
    if (l.empty()) {
        cout << "list是空的";
    } else {
        cout << "list有" << l.size() << "个元素\n"; // 输出:3
    }

    // 2. 取元素:front(第一个)、back(最后一个)
    cout << "第一个元素:" << l.front() << endl; // 输出:1
    cout << "最后一个元素:" << l.back() << endl; // 输出:3

    // 新手小技巧:front/back返回的是引用,能直接修改元素
    l.front() = 100;
    cout << "修改后第一个元素:" << l.front() << endl; // 输出:100
    return 0;
}

2.4 第四步:增删改查(list 的核心优势)
这是 list 最牛的地方 —— 任意位置增删都超快,新手先掌握这些高频接口:

int main() {
    list<int> l = {1,2,3};

    // 1. 首尾增删(最简单)
    l.push_front(0); // 头部加0 → {0,1,2,3}
    l.push_back(4);  // 尾部加4 → {0,1,2,3,4}
    l.pop_front();   // 删头部 → {1,2,3,4}
    l.pop_back();    // 删尾部 → {1,2,3}

    // 2. 任意位置插入(新手重点)
    // 先找到要插入的位置(比如第二个元素)
    auto it = l.begin();
    ++it; // 从第一个元素走到第二个元素的位置
    l.insert(it, 99); // 在第二个位置插99 → {1,99,2,3}

    // 3. 任意位置删除
    it = l.begin();
    ++it; // 指向99的位置
    l.erase(it); // 删除99 → {1,2,3}

    // 4. 清空list
    // l.clear(); // 清空后list为空,size=0

    // 遍历看看最终结果
    for (auto num : l) { // 新手简化写法:范围for循环
        cout << num << " "; // 输出:1 2 3
    }
    return 0;
}

💡 小提醒:

  • 范围 for 循环(for (auto num : l))是 C++11 的特性,底层还是迭代器,新手用这个遍历更简单;
  • 插入 / 删除时,先找迭代器位置,再调用接口,别直接写数字(比如l.insert(2, 99)会报错,因为 list 不支持随机访问)。

3. 【踩坑预警】迭代器失效?新手最容易栽的坑
这是新手学 list 最容易出错的地方!先搞懂 “迭代器失效” 的本质:迭代器指向的节点被删了,这个 “小手” 就抓空了,再用就会崩溃。

int main() {
    list<int> l = {1,2,3,4};
    auto it = l.begin();
    while (it != l.end()) {
        l.erase(it); // 删完it指向的节点,it就失效了
        ++it; // 报错!失效的迭代器不能++
    }
    return 0;
}

3.2 正确写法(必记)
有两种写法,推荐第一种(简单好记):

// 写法1:erase(it++) → 先把it传给erase,再让it自增(此时it还没失效)
int main() {
    list<int> l = {1,2,3,4};
    auto it = l.begin();
    while (it != l.end()) {
        l.erase(it++); // 安全!
    }
    cout << l.size(); // 输出:0
    return 0;
}

// 写法2:it = l.erase(it) → erase返回下一个有效迭代器
int main() {
    list<int> l = {1,2,3,4};
    auto it = l.begin();
    while (it != l.end()) {
        it = l.erase(it); // 接收返回值,it指向新的有效位置
    }
    return 0;
}

3.3 必记的迭代器失效规则
在这里插入图片描述
4. 【动手实操】新手也能看懂的 list 模拟实现
学完用法,新手可以简单了解 list 的底层实现(不用全写,看懂核心逻辑就行),能帮你更好理解为啥 list 这么用。

4.1 第一步:定义节点结构(小朋友的 “牵手信息”)
每个节点要存 “自己的数据”“前面的人”“后面的人”:

// 模板类:支持存任意类型(int/string等)
template<class T>
struct ListNode {
    // 节点构造函数(新手:T()是默认值,比如int就是0)
    ListNode(const T& val = T()) 
        : _val(val), _prev(nullptr), _next(nullptr) {}

    T _val;          // 自己的数据
    ListNode* _prev; // 前面的节点
    ListNode* _next; // 后面的节点
};

4.2 第二步:封装迭代器(新手的 “小手”)
因为 list 的节点不是连续内存,不能直接用指针,所以要封装迭代器,重载++/–/*这些操作:

template<class T, class Ref, class Ptr>
struct ListIterator {
    typedef ListNode<T> Node;
    Node* _node; // 指向节点的指针

    // 构造:给迭代器绑定一个节点
    ListIterator(Node* node) : _node(node) {}

    // 解引用:拿到节点里的数据(新手:Ref是引用,能修改)
    Ref operator*() { return _node->_val; }

    // 迭代器往后走(++)
    ListIterator& operator++() {
        _node = _node->_next; // 指向后面的节点
        return *this;
    }

    // 迭代器往前⾛(--)
    ListIterator& operator--() {
        _node = _node->_prev; // 指向前面的节点
        return *this;
    }

    // 判断两个迭代器是否相等
    bool operator!=(const ListIterator& it) {
        return _node != it._node;
    }
};

4.3 第三步:list 主体类(管理整个链表)
核心是 “头结点”(不存数据,只用来统一操作),封装构造、迭代器、增删等接口:

template<class T>
class List {
    typedef ListNode<T> Node;
public:
    // 迭代器类型定义(新手:简化写法)
    typedef ListIterator<T, T&, T*> iterator;

    // 构造空list:创建头结点,自己指向自己(双向循环)
    List() {
        _head = new Node();
        _head->_prev = _head;
        _head->_next = _head;
    }

    // 迭代器接口:返回头结点后面的第一个节点
    iterator begin() { return iterator(_head->_next); }
    iterator end() { return iterator(_head); } // 尾是头结点

    // 尾部插入(新手简化版)
    void push_back(const T& val) {
        Node* tail = _head->_prev; // 找到最后一个节点
        Node* new_node = new Node(val); // 新建节点

        // 改指针:尾节点→新节点→头结点
        tail->_next = new_node;
        new_node->_prev = tail;
        new_node->_next = _head;
        _head->_prev = new_node;
    }

private:
    Node* _head; // 头结点
};

4.4 新手测试模拟实现的 list

int main() {
    List<int> l;
    l.push_back(1);
    l.push_back(2);
    l.push_back(3);

    // 遍历(和STL的list用法一样!)
    for (auto it = l.begin(); it != l.end(); it++) {
        cout << *it << " "; // 输出:1 2 3
    }
    return 0;
}

💡 小总结:模拟实现不用写全,只要知道 “list 是节点拼起来的,迭代器是封装的指针”,就能理解为啥 list 不能随机访问、增删快。

5. 【终极对比】list vs vector:啥时候用谁?
新手最纠结 “该用 list 还是 vector”,记住这几个场景就行,不用死记原理:
在这里插入图片描述

// 场景1:查成绩(要随机访问)→ 用vector
vector<int> scores = {90,85,95,88};
cout << "第3个同学的成绩:" << scores[2] << endl; // 直接访问

// 场景2:排队买票(频繁有人插队/离开)→ 用list
list<string> queue;
queue.push_back("小明");
queue.push_back("小红");
auto it = queue.begin();
queue.insert(it, "小刚"); // 小刚插队到第一个

6. 【复习小结】核心知识点一键回顾
总结
1.list 底层是双向循环链表,核心优势是任意位置 O (1) 增删,核心短板是不支持随机访问;
2.迭代器失效是新手最大坑:list 删除后只有被删节点的迭代器失效,推荐用l.erase(it++);
3.选容器的核心原则:查得多用 vector,增删多用 list;
4.模拟实现不用全写,记住 “节点存数据 + 前后指针,迭代器封装指针” 就行;
5.新手用 list 的高频接口:push_back/push_front/insert/erase/begin/end。

PCL RANSAC拟合直线并插值 空间直线线性插值的原理与实现方法。 阅读详情

相关推荐

ESP8266+ITR8307实战:如何用反射式光电管实现5cm远距离检测(附避坑指南)

本文详细介绍了如何利用ESP8266的ADC功能与ITR8307反射式光电管,通过调制解调、硬件电路优化及软件同步采样策略,实现5cm稳定远距离检测。文章提供了完整的硬件设计、代码实现与调优避坑指南,有效解决了环境光干扰与信号衰减难题,适用于智能车、自动化等需要可靠非接触检测的项目。

rgv234567的博客 264

【C++】10. list

本篇文章对list的各种接口进行了总结,以及通过模拟实现了list的部分接口。

2301_80475881的博客 619

【Shader】Unity批量更换Shader

在Unity开发中,因为渲染管线问题,经常会遇到需要大量更改shader的问题,本篇文章讲述批量更换Shader的工具。

憨辰 1466

C++知识点总结10 list

为什么会有list? 补充vector的不足 vector的优点: 支持下标的随机访问(operator[]),间接的很好的支持排序、二分查找、堆算法等 vector的缺点: 1、头部和中部的插入删除效率低,O(N),因为需要挪动数据 2、插入数据空间不够需要增容,增容需要开辟新空间、拷贝数据、释放旧空间,会付出很大的代价 list的优点: 1、头部、中间插入不需要挪动数据,效率高,O(1) 2、插...

weixin_47641376的博客 313

RK3568 Android12预置APK的三种模式详解:不可卸载、可卸载与永久删除

本文深入解析RK3568 Android12系统中APK预置的三种模式:不可卸载、可卸载与永久删除,详细介绍了每种模式的技术实现、应用场景及实战案例。针对Rockchip平台特性,提供从目录创建到系统编译的完整指南,帮助开发者根据产品需求选择最佳预置方案,并解决常见问题。

k8s6orchestrator的博客 967

中国 it 集成商 主要有哪些 主要做什么工作

既要懂技术(云计算/AI),又要吃透行业(金融/政务)。:用国产化方案(麒麟OS+达梦数据库)替换Oracle/Windows。:如为银行搭建数据中心(采购华为服务器+思科交换机)。:数据中心监控、故障处理(如华胜天成服务国家电网)。:核心交易系统(如中科软为保险公司开发精算平台)。:智慧城市(如太极股份承建“一网通办”政务平台)。:HIS医院信息系统(如东华软件的电子病历系统)。:硬件透明化(如服务器价格战)压缩集成毛利。:工厂设备联网(新华三的工业互联网平台)。

hxsln11的博客 3038

【重走C++学习之路】10、STL-list

list的常见接口和模拟实现,list的迭代器的封装,vector和list的区别

MeiYu_123的博客 919

【人工智能】AI驱动值班巡检Agent(Hermes)

传统运维值班巡检场景中,存在四大核心痛点:本项目基于 Agent+Skills+浏览器自动化 架构,构建了具备自我进化能力的AI值班员,核心逻辑流如下:

奶爸的编程之路,也就一周冷个三天 178

c++ - 第10节 - list类

注:1.普通正向迭代器和普通反向迭代器的使用方式如下图所示。

qq_45113223的博客 1274

喷涂机器人系列编程:ABB IRB 5500_(2).ABB IRB 5500喷涂机器人系统架构与组件

ABB IRB 5500喷涂机器人系统不仅在性能上表现出色,而且在安全和维护方面也提供了全面的解决方案。通过本文的详细介绍,希望读者能够更好地理解和掌握这一系统的架构和功能,为实际应用提供有力支持。

zhubeibei168的博客 778

【部署】Ubuntu 24.04.4虚拟机下部署OpenClaw

OpenClaw是一款基于AI的智能助手工具,本文详细介绍了在Ubuntu 24.04.4系统上的三种安装方法。推荐使用一键安装脚本(curl -fsSL https://openclaw.ai/install.sh | bash),自动完成Node.js检测、CLI安装和引导配置。对于开发者,提供了从源码构建的详细步骤,包括环境准备、依赖安装和构建流程。文章还包含常见问题解决方案(如sharp构建错误处理)以及后续维护建议(更新、配置检查和日志查看)。安装完成后,用户可通过终端TUI或Web UI进行初始

海兰 1万+

C++基础10:STL容器list

1.基础操作

weixin_42278523的博客 190

【图像去噪】论文复现:大道至简!NAFNet的Pytorch源码复现!跑通NAFNet源码,补充PlainNet,由大到小拆解NAFNet网络结构,由小到大实现结构组合,逐行注释!

论文题目:Simple Baselines for Image Restoration —— 图像恢复的简单基线【图像去噪】论文精读:Simple Baselines for Image Restoration(NAFNet)本文以SIDD数据集为例,跑通NAFNet源码,详解NAFNet网络结构与实现。建议与KBNet和CGNet的复现文件综合查看。训练和测试代码解析见单独的BasicSR源码精读(后续会出)。

主要更新底层视觉(去噪、超分等)相关的科研内容,形式为【论文精读】+【论文复现】 2993

【c++_containers】10分钟带你学会list

本文将会从list的特性、模拟实现,与vector的对比带你熟练掌握list的使用

m0_73495584的博客 1058

jre9下载 jdk9

(Java SE Runtime Environment 9.0) 9.0u175 官方最新版 【x86|x64】

复分析 阿尔福斯(Ahlfors) 第三版答案 英文

答案分为十章,有部分习题没有答案,全英文复分析阿尔福斯(Ahlfors) 第三版答案

PyTorch C++ c10 核心类型详解:从 ArrayRef、Optional 到 Dict、List、IListRef、Half 与 IValue 的源码级指南

c10(Core10,意为"PyTorch 的底层十库")提供了整个 PyTorch 运行时共享的基础设施,其中 [Core Types 官方文档](https://link.gitcode.com/i/8c367a64d87f6b020cd25c71cc17ea68) 所归纳的 `ArrayRef`、`OptionalArrayRef`、`c10::optional`、`Half`、容器(`Di

gitblog_00599的博客 749

ET2012新版免狗免安装绿色软件

ET2012新版免狗免安祖昂绿色软件。,新界面,新视觉,

C++ //练习 10.39 list上的迭代器属于哪类?vector呢?

C++ Primer(第5版) 练习 10.39

Made in Code 229

C++ STL list 与手写链表性能对比:10万次插入/删除耗时实测

本文通过10万次插入/删除操作的实测数据,对比了C++ STL list与手写链表的性能差异。测试结果显示,STL list在综合性能和内存管理上表现优异,而手写链表在特定场景下可优化至更高效率。文章为开发者提供了详细的性能分析及工程实践建议,帮助在链表数据结构选择上做出最优决策。

weixin_34378969的博客 369

10. C++STL详解list的使用以及模拟实现

C++STL详解list的使用以及模拟实现

分享C/C++/Linux/网络/数据库/数据结构 1153

C++初阶(10)/STL(三) :list

文章聚焦C++标准库双向链表容器std::list,剖析其核心特性与使用要点。阐述其作为双向链表的特性,如O(1)时间复杂度的任意位置插入/删除、不支持随机访问等;详解构造方法、迭代器操作等常用接口,强调insert/erase用法及迭代器失效问题;展示反向迭代器、链表节点等模拟实现细节;并与vector从底层结构、性能、空间利用等方面展开全面对比。

2401_86650143的博客 1102

C++初阶10:list底层原理大揭秘

本文详细介绍了C++ STL中list容器的模拟实现过程。首先剖析了list的底层结构,采用双向循环链表设计,重点讲解了节点结构封装和空链表实现。其次深入探讨了迭代器的核心特性,通过运算符重载模拟指针行为,并实现了普通迭代器和const迭代器的封装。最后详细阐述了list的核心功能实现,包括构造析构、深拷贝、增删改查等操作,特别强调了insert和erase作为核心接口的重要性。整个过程遵循"底层原理拆解→核心组件实现→功能完善→测试验证"的开发逻辑,为理解STL容器实现提供了清晰的技术

chen060603的博客 326

C++ //练习 10.36 使用find在一个int的list中查找最后一个值为0的元素。

C++ Primer(第5版) 练习 10.36

Made in Code 604

C++ //练习 10.42 使用list代替vector重新实现10.2.3节(第343页)中的去除重复单词的程序。

【代码】C++ //练习 10.42 使用list代替vector重新实现10.2.3节(第343页)中的去除重复单词的程序。

Made in Code 277

41 C++ STL模板库10-容器3-list

本文介绍了C++ STL中的list双向链表容器,主要包含三部分内容:1. 构造函数与赋值操作,详细说明了默认构造、填充构造、迭代器构造等初始化方式以及assign()、operator=等赋值操作;2. 元素访问方法,包括front()/back()访问首尾元素,以及迭代器遍历等访问方式;3. 迭代器操作,涵盖正向/反向迭代器的使用和元素修改方法。list作为双向链表,支持O(1)时间复杂度的插入/删除操作,适合频繁修改场景,但不支持随机访问。文章通过代码示例展示了各种操作的用法和注意事项。

17208955的博客 1241

10.C++ STL之forward_list

forward_list是单向链表 list是双向链表,forward_list与其相类。

CarmenIsOK的博客 253

C/C++ 入门(10)list类(STL)

欢迎来指教!

敲代码嘞~的博客 1070

C++STL初阶(10):list的简易实现(下)

在上一文中我们完成了链表的多数基本接口,本文主要围绕构造函数进行补充。

2301_79501467的博客 864

深挖C++:类和对象(中)

(先进后出)

xys_678的博客 245
上一篇: C++9(vector)
下一篇: C++11(stack和queue)
code_whiter
博客等级 码龄2年 289粉丝 50原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值