当前位置:首页 > 系统教程 > 正文

C++领域展开第十八幕——STL list容器的了解和使用以及手撕模拟 超详细小白也能学会的list容器教程

C++领域展开第十八幕——STL list容器的了解和使用以及手撕模拟 超详细小白也能学会的list容器教程

欢迎来到C++领域第十八幕!今天我们将深入探讨STL中的list容器,从了解到使用,最后手撕模拟实现,带你彻底掌握这一重要容器。无论你是C++初学者还是有一定经验的开发者,相信这篇文章都能让你对list有更深刻的理解。

一、STL list容器概述

STL list容器是C++标准模板库中的序列式容器之一,其底层实现为双向循环链表。与vector的连续内存不同,list的元素在内存中分散存储,通过指针链接。这使得list在任意位置插入和删除元素都非常高效(O(1)时间复杂度),但缺点是无法随机访问元素(不支持operator[]),只能通过迭代器顺序访问。list非常适合频繁插入删除的场景,如实现LRU缓存、任务队列等。

C++领域展开第十八幕——STL list容器的了解和使用以及手撕模拟 超详细小白也能学会的list容器教程 STL list容器  C++ list使用 list迭代器 list模拟实现 第1张

上图展示了list的双向链表结构,每个节点包含数据域data、前驱指针prev和后继指针next。通过头节点(哨兵)可以快速访问首尾元素。

二、list的构造和赋值

list提供了多种构造函数:默认构造、填充构造、范围构造、拷贝构造等。下面是一些示例:

    #include std::list l1;                     // 空liststd::list l2(5, 10);              // 5个10std::list l3(l2.begin(), l2.end()); // 迭代器范围std::list l4(l3);                  // 拷贝构造std::list l5 = {1,2,3,4,5};        // C++11初始化列表  

赋值操作可使用operator=或assign成员函数:l1 = l2; l1.assign(3, 100); 等。

三、list的常用操作

list的成员函数非常丰富,下面分类介绍C++ list使用中的常见操作:

  • 元素访问:front()、back() 返回首尾元素的引用。
  • 插入:push_front(val)、push_back(val)、insert(pos, val)(在pos前插入)、emplace_front/back/insert(直接构造)。
  • 删除:pop_front()、pop_back()、erase(pos) 或 erase(first,last)、clear() 清空所有元素。
  • 大小:size()、empty()、max_size()、resize(n) 改变大小。
  • 其他操作:reverse() 反转链表、sort() 排序(注意list有自己专用的sort,因为全局sort需要随机访问迭代器)、merge() 合并两个有序链表、unique() 去除连续重复元素、remove(val) 删除所有等于val的元素、splice() 转移元素等。

例如:l.push_back(42); l.pop_front(); l.insert(++l.begin(), 99);

四、list迭代器详解

list迭代器属于双向迭代器,支持++和--操作,但不支持随机访问(如it+5)。list的迭代器不会因为插入或其他删除(除非指向被删元素)而失效,这一点与vector不同。使用迭代器遍历list:

    for(auto it = l.begin(); it != l.end(); ++it) {    std::cout << *it << " ";}  

list还提供反向迭代器rbegin/rend。

五、手撕模拟:实现一个简易list

理解了list的原理后,我们可以尝试list模拟实现,以加深对底层机制的理解。下面是一个简化版的mylist,包含节点结构、迭代器封装和基本操作。注意:为了清晰,省略了一些异常安全和C++11特性。

    template struct ListNode {    T data;    ListNode* prev;    ListNode* next;    ListNode(const T& val = T(), ListNode* p = nullptr, ListNode* n = nullptr)        : data(val), prev(p), next(n) {}};template class ListIterator {public:    using iterator_category = std::bidirectional_iterator_tag;    using value_type = T;    using difference_type = ptrdiff_t;    using pointer = T;    using reference = T&;    ListNode ptr;    ListIterator(ListNode* p = nullptr) : ptr(p) {}    reference operator*() const { return ptr->data; }    pointer operator->() const { return &(ptr->data); }    ListIterator& operator++() { ptr = ptr->next; return *this; }    ListIterator operator++(int) { ListIterator tmp = this; ++(this); return tmp; }    ListIterator& operator--() { ptr = ptr->prev; return this; }    ListIterator operator--(int) { ListIterator tmp = this; --(this); return tmp; }    bool operator==(const ListIterator& other) const { return ptr == other.ptr; }    bool operator!=(const ListIterator& other) const { return ptr != other.ptr; }};template class mylist {    using Node = ListNode;    Node head; // 哨兵节点,head->next指向第一个元素,head->prev指向最后一个元素    size_t sz;public:    using iterator = ListIterator;    using const_iterator = ListIterator; // 简化,实际需要区分    mylist() : head(new Node()), sz(0) {        head->prev = head;        head->next = head;    }    ~mylist() { clear(); delete head; }    void clear() {        while (!empty()) pop_front();    }    bool empty() const { return sz == 0; }    size_t size() const { return sz; }    iterator begin() { return iterator(head->next); }    iterator end() { return iterator(head); }    const_iterator begin() const { return const_iterator(head->next); }    const_iterator end() const { return const_iterator(head); }    void push_front(const T& val) { insert(begin(), val); }    void push_back(const T& val) { insert(end(), val); }    void pop_front() { erase(begin()); }    void pop_back() { erase(--end()); }    iterator insert(iterator pos, const T& val) {        Node* cur = pos.ptr;        Node* prev = cur->prev;        Node* newNode = new Node(val, prev, cur);        prev->next = newNode;        cur->prev = newNode;        ++sz;        return iterator(newNode);    }    iterator erase(iterator pos) {        Node* cur = pos.ptr;        if (cur == head) return end(); // 不能删除哨兵        Node* prev = cur->prev;        Node* next = cur->next;        prev->next = next;        next->prev = prev;        delete cur;        --sz;        return iterator(next);    }    // 其他功能如拷贝构造、赋值运算符等省略};  

上述代码实现了list的核心框架,包括迭代器和基本插入删除。通过模拟实现,我们可以更清晰地理解list的内存管理和迭代器原理。

总结

本文详细介绍了STL list容器的概念、构造、常用操作、迭代器以及手撕模拟实现。list作为双向链表,在频繁插入删除的场景下具有显著优势,但需注意其不支持随机访问的特点。希望通过本教程,你对list有了更全面的掌握,能够灵活运用并理解其底层原理。

本文关键词:STL list容器 C++ list使用 list迭代器 list模拟实现