List(类函数学习)

1.1 list介绍

std::list 是 C++ 标准库中提供的一个双向链表容器,属于序列容器的一种。它基于带头结点的双向循环链表实现,支持在任意位置高效地插入和删除元素,时间复杂度为 O(1),但不支持随机访问。

std::vector 不同,std::list 的元素在内存中不是连续存储的,每个节点包含数据域和两个指针(前驱和后继),因此其空间开销较大,但在频繁插入/删除操作场景下性能更优。

关键特性:

  • 支持双向遍历
  • 插入、删除操作不会导致其他迭代器失效(除被删除节点对应的迭代器)
  • 不支持下标访问(如 list[i]
  • 可以在任意位置插入或删除元素

#1.2 list的使用

1.2.1 list的构造

构造函数( (constructor) 接口说明
list (size_type n, const value_type& val =value_type()) 构造的list中包含n个值为val的元素
list() 构造空的list
list (const list& x) 拷贝构造函数
list (InputIterator first, InputIterator last) 用[first, last)区间中的元素构造list
cpp 复制代码
#include <iostream>
#include <list>

int main() {
    // 1. 构造空的 list
    std::list<int> lst1;
    std::cout << "lst1.size() = " << lst1.size() << std::endl; // 0

    // 2. 构造 n 个值为 val 的元素
    std::list<int> lst2(5, 10); // 5 个 10
    for (int x : lst2) std::cout << x << " "; // 10 10 10 10 10
    std::cout << std::endl;

    // 3. 拷贝构造
    std::list<int> lst3(lst2);
    std::cout << "lst3.size() = " << lst3.size() << std::endl; // 5

    // 4. 用迭代器区间构造(支持任何输入迭代器)
    std::list<int> lst4{1, 2, 3, 4, 5};
    std::list<int> lst5(lst4.begin(), lst4.end());

    return 0;
}

重点说明:

  • 使用 std::list<int>(n, val) 可快速初始化多个相同值。
  • 拷贝构造和范围构造均能保留原始数据结构。
  • 初始容量由实际元素数量决定,无需预分配。

1.2.2 list iterator的使用

函数声明 接口说明
begin + end 返回第一个元素的迭代器+返回最后一个元素下一个位置的迭代器
rbegin + rend 返回第一个元素的reverse_iterator,即end位置,返回最后一个元素下一个位置的reverse_iterator,即begin位置

std::list 提供多种迭代器类型,用于正向与反向遍历。

cpp 复制代码
#include <iostream>
#include <list>

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

    // 正向迭代器:begin() -> end()
    std::cout << "正向遍历: ";
    for (auto it = lst.begin(); it != lst.end(); ++it) {
        std::cout << *it << " ";
    }
    std::cout << std::endl;

    // 反向迭代器:rbegin() -> rend()
    std::cout << "反向遍历: ";
    for (auto rit = lst.rbegin(); rit != lst.rend(); ++rit) {
        std::cout << *rit << " ";
    }
    std::cout << std::endl;

    // 举例:修改元素(通过迭代器)
    auto it = lst.begin();
    ++it; // 指向第二个元素 20
    *it = 99; // 将 20 改为 99

    std::cout << "修改后: ";
    for (const auto& x : lst) std::cout << x << " ";
    std::cout << std::endl;

    return 0;
}

核心要点:

  • begin() 指向第一个有效元素,end() 指向最后一个元素之后的位置。
  • rbegin() 指向最后一个元素(即原 end() 的前一个),rend() 指向第一个元素之前。
  • 对正向迭代器执行 ++ 向后移动;对反向迭代器执行 ++ 实际是向前移动。
  • 迭代器可直接解引用并修改内容。

1.2.3 list capacity

函数声明 接口说明
empty 检测list是否为空,是返回true,否则返回false
size 返回list中有效节点的个数

判断容器状态的方法,常用于条件判断。

cpp 复制代码
#include <iostream>
#include <list>

int main() {
    std::list<int> lst;

    std::cout << "是否为空?" << (lst.empty() ? "是" : "否") << std::endl; // 是

    lst.push_back(1);
    lst.push_back(2);

    std::cout << "当前大小: " << lst.size() << std::endl; // 2

    lst.clear();
    std::cout << "清空后大小: " << lst.size() << std::endl; // 0

    return 0;
}

注意事项:

  • empty() 时间复杂度为 O(1),仅检查头结点是否指向自身。
  • size() 返回有效节点数,非容量上限。
  • clear() 清空所有元素,但不释放底层内存(若需释放,可配合 swap 或析构)。

1.2.4 list element access

函数声明 接口说明
front 返回list的第一个节点中值的引用
back 返回list的最后一个节点中值的引用

由于 list 不支持随机访问,只能通过首尾元素获取值。

cpp 复制代码
#include <iostream>
#include <list>

int main() {
    std::list<int> lst = {100, 200, 300};

    if (!lst.empty()) {
        std::cout << "front(): " << lst.front() << std::endl; // 100
        std::cout << "back(): " << lst.back() << std::endl;   // 300
    }

    // 可修改首尾元素
    lst.front() = 999;
    lst.back() = 888;

    std::cout << "修改后: ";
    for (const auto& x : lst) std::cout << x << " ";
    std::cout << std::endl;

    return 0;
}

重要提示:

  • front()back() 返回的是引用,可作为左值使用。
  • 若调用空列表的 front() / back(),将引发未定义行为。
  • 建议先调用 empty() 判断再访问。

1.2.5 list modifiers

函数声明 接口说明
push_front 在list首元素前插入值为val的元素
pop_front 删除list中第一个元素
push_back 在list尾部插入值为val的元素
pop_back 删除list中最后一个元素
insert 在list position 位置中插入值为val的元素
erase 删除list position位置的元素
swap 交换两个list中的元素
clear 清空list中的有效元素

提供丰富的插入与删除接口,适用于动态数据管理。

cpp 复制代码
#include <iostream>
#include <list>

int main() {
    std::list<int> lst = {10, 20, 30};

    // 插入操作
    lst.push_front(5);     // 头部插入 5 → [5, 10, 20, 30]
    lst.push_back(40);     // 尾部插入 40 → [5, 10, 20, 30, 40]

    // 在指定位置插入
    auto it = lst.begin();
    ++it; // 指向 10
    lst.insert(it, 15); // 插入 15 → [5, 10, 15, 20, 30, 40]

    // 删除操作
    lst.pop_front();       // 移除头部 5 → [10, 15, 20, 30, 40]
    lst.pop_back();        // 移除尾部 40 → [10, 15, 20, 30]

    // 删除指定位置元素
    it = lst.begin();
    ++it; // 指向 15
    lst.erase(it); // 移除 15 → [10, 20, 30]

    // 交换两个 list
    std::list<int> lst2 = {1, 2, 3};
    lst.swap(lst2); // 交换内容

    std::cout << "lst 内容: ";
    for (const auto& x : lst) std::cout << x << " ";
    std::cout << std::endl;

    return 0;
}

关键点总结:

  • push_front / push_back:分别在头尾添加元素。
  • insert(pos, val):在 pos 前插入一个新元素。
  • erase(pos):删除 pos 所指元素,返回下一个迭代器。
  • swap:交换两个 list 内容,效率高(仅交换指针)。
  • clear():移除所有元素,但不改变容器本身。

1.2.6 list的迭代器失效

这是 std::list 最重要的特性之一。

cpp 复制代码
#include <iostream>
#include <list>

int main() {
    std::list<int> lst = {1, 2, 3, 4, 5};

    auto it = lst.begin();
    ++it; // 指向 2

    // 删除 it 所指元素(即 2)
    lst.erase(it);

    // 此时 it 已经失效!不能再使用
    // 下面代码会导致未定义行为:
    // std::cout << *it << std::endl; // ❌ 危险!

    // 安全做法:重新获取迭代器
    auto new_it = lst.begin();
    ++new_it; // 现在指向原来的 3
    std::cout << "新的迭代器指向: " << *new_it << std::endl; // 3

    return 0;
}

核心结论:

  • 插入操作不会使任何迭代器失效,因为链表节点独立分配。

  • 删除操作仅使被删除节点的迭代器失效,其余迭代器仍有效。

  • 被删除节点的迭代器不可再使用,否则造成未定义行为。

  • 推荐在 erase 后立即更新迭代器,例如:

    cpp 复制代码
    it = lst.erase(it); // erase 返回下一个有效迭代器

总结:list的核心优势与适用场景

特性 说明
✅ 高效插入/删除 任意位置插入删除均为 O(1)
✅ 迭代器安全(除被删节点外) 其他迭代器不受影响
❌ 不支持随机访问 无法使用 lst[i]
❌ 内存开销大 每个节点额外存储前后指针
⚠️ 适合场景 频繁增删、需要保持顺序、不需要随机访问

典型应用场景:

  • 实现队列、栈(结合 push_front / pop_front
  • 动态维护有序集合(如任务调度)
  • 需要频繁在中间插入/删除的数据结构

补充建议:最佳实践

cpp 复制代码
// 推荐写法:使用 erase + 返回值
for (auto it = lst.begin(); it != lst.end(); ) {
    if (*it % 2 == 0) {
        it = lst.erase(it); // erase 返回下一个迭代器
    } else {
        ++it;
    }
}

// 错误写法(可能导致迭代器失效)
for (auto it = lst.begin(); it != lst.end(); ++it) {
    if (*it % 2 == 0) {
        lst.erase(it); // 此时 it 已失效!后续 ++it 会出错
    }
}

记住:

  • 删除元素后,不要继续使用该迭代器。
  • 使用 erase 的返回值来更新迭代器,是最安全的方式。

✅ 本文全面覆盖了 std::list 的构造、迭代器、容量、元素访问、修改操作及迭代器失效机制,并辅以真实可运行代码示例,帮助开发者深入理解其行为与最佳实践。

相关推荐
Dr_Fourier1 小时前
AWQ量化
c++·人工智能·pytorch·ai
江屿风1 小时前
【Linux系统】【Linux 进程程序替换机制解析及自定义 Shell 核心逻辑实现 】流食般投喂
linux·运维·服务器·开发语言·笔记
彧azz11 小时前
算法设计与分析:贪心与动态规划
数据结构·学习·算法·贪心算法·动态规划
一条小小yu13 小时前
基于数据库唯一索引 + 过期时间的分布式锁
学习
汉克老师13 小时前
GESP2026年9月认证C++六级( 第三部分编程题(1、数组划分))精讲
c++·gesp·小学生·学c++编程
传奇开心果编程13 小时前
【ArkUI 练中学】第15课:UI 界面设计与实战
学习·ui·华为·harmonyos
千里码aicood13 小时前
基于PLC与机器视觉的智能分拣控制系统设计
c++·自动化·plc
zjxtxdy13 小时前
SPI通信协议
笔记·单片机
漂流瓶jz13 小时前
UVA-1442 洞穴 题解答案代码 算法竞赛入门经典第二版
c++·算法·题解·aoapc·算法竞赛入门经典·uva