STL list

本文详细解析了C++标准库中的list容器,包括其节点结构、迭代器设计、重要性质以及如何实现迁移操作等核心内容。通过深入探讨,读者可以更熟练地掌握list容器的使用技巧。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

list的节点结构:
template <class T>
struct __list_node {
  typedef void* void_pointer;
  void_pointer next;
  void_pointer prev;
  T data;
};

List不能像vector那样以普通的指针作为iterator,因为节点不能保证内存在空间上是连续的。
List是一个双向链表,所以需要有前移后移的功能,所以list提供的Bidirectional_iterator迭代器。
list的最重要性质就是insert操作和接合操作(splice)都不会造成list迭代器失效,这在vector上是不成立的。list的删除只会导致指向被删除元素的那个迭代器失效,其他迭代器不会失效。
// list的迭代器设计
template<class T, class Ref, class Ptr>
struct __list_iterator {
  typedef __list_iterator<T, T&, T*> iterator;
  typedef __list_iterator<T, const T&, const T*> const_iterator;
  typedef __list_iterator<T, Ref, Ptr> self;
  typedef bidirectional_iterator_tag iterator_category; // Bidirectional_iterator型别迭代器
  typedef T value_type;
  typedef Ptr pointer;
  typedef Ref reference;
  typedef __list_node<T>* link_type;  // iterator内部的link类型是指针类型  
  typedef size_t size_type; 
  typedef ptrdiff_t difference_type;
  link_type node;   // 迭代器内部指向node的指针
  __list_iterator(link_type x) : node(x) {}
  __list_iterator() {}
  __list_iterator(const iterator& x) : node(x.node) {}
  bool operator==(const self& x) const { return node == x.node; }
  bool operator!=(const self& x) const { return node != x.node; }
// 一下对迭代器取值,取得是节点的data
  reference operator*() const { return (*node).data; }
#ifndef __SGI_STL_NO_ARROW_OPERATOR
  pointer operator->() const { return &(operator*()); }
#endif /* __SGI_STL_NO_ARROW_OPERATOR */
  self& operator++() {
    node = (link_type)((*node).next);
    return *this;
  }
// 前进一个节点
  self operator++(int) {
    self tmp = *this;
    ++*this;
    return tmp;
  }
  self& operator--() {
    node = (link_type)((*node).prev);
    return *this;
  }
  self operator--(int) {
    self tmp = *this;
    --*this;
    return tmp;
  }
};

template <class T, class Alloc = alloc>  // 和vector一样默认使用alloc作为内存配置器
class list {
protected:
  typedef __list_node<T> list_node;
  typedef list_node* link_type;
...
protected:
  link_type node; // 只要一个指针就表示了整个环状的双向链表
};

整个list的示意图,只要刻意在环状链表的尾端加上一个空白节点,变符合stl规范的“前闭后开”[first, last )区间的规范,
这样几个函数变很容易的实现了
iterator begin() { return (link_type)((*node).next); }
iterator end() { return node; }
bool empty() const { return node->next == node; }
size_type size() const {
    size_type result = 0;
    distance(begin(), end(), result);
    return result;
 }
//取得是节点的内容(元素值)list_iterator中operator*()取得是节点的data
 reference front() { return *begin(); }
 //注意iterator是前开后闭区间,所有是*--end()是最后一个元素的内容
  reference back() { return *(--end()); }

对以list的结构有很清晰了解之后,相信对于list的使用就很简单了
另外补充一点:
 // list内部(非公开接口)提供了迁移操作,将某些连续范围[first, last)的元素迁移到某个特定位置position之前,技术上也很简单就是指针的移动
// 其他很多复杂的操作如splice(接合,将连续范围的元素从一个list移动到另外一个list的某个点),sort,merge, reverse等等操作都是在这个基础上实现
// STL算法sort只接受RamdonAccessIterator,所以list实现了一个自己sort member function.采用的是quick sort
  void transfer(iterator position, iterator first, iterator last) {
    if (position != last) {
      (*(link_type((*last.node).prev))).next = position.node;
      (*(link_type((*first.node).prev))).next = last.node;
      (*(link_type((*position.node).prev))).next = first.node;
      link_type tmp = link_type((*position.node).prev);
      (*position.node).prev = (*last.node).prev;
      (*last.node).prev = (*first.node).prev;
      (*first.node).prev = tmp;
    }
  }

// 逆向重置reverse,使用transfer很简单的实现了o(n)
// 其算法核心是历遍链表, 每次取出一个结点, 并插入到链表起始点  
// 历遍完成后链表满足倒置
template <class T, class Alloc>
void list<T, Alloc>::reverse() {
  if (node->next == node || link_type(node->next)->next == node) return;
  iterator first = begin();
  ++first;
  while (first != end()) {
    iterator old = first;
    ++first;
    transfer(begin(), old, first);
  }
}  



### C++ STL List 使用与实现 #### 定义与基本特性 `std::list` 是双向链表容器,允许高效的插入和删除操作。不同于 `vector` 或者 `deque` 这样的连续内存存储结构,`list` 的元素分布在不同的位置上,通过指针相互链接[^1]。 ```cpp #include <iostream> #include <list> int main() { std::list<int> myList; // 插入元素到列表中 myList.push_back(10); myList.push_front(20); // 输出列表中的第一个和最后一个元素 std::cout << "Front element: " << myList.front() << "\n"; std::cout << "Back element: " << myList.back() << "\n"; return 0; } ``` #### 基本操作函数 - **push_back**: 向列表尾部添加新元素。 - **push_front**: 向列表头部添加新元素。 - **pop_back**: 移除列表尾部的元素。 - **pop_front**: 移除列表头部的元素。 - **insert**: 在指定迭代器前插入新的元素或范围内的多个元素。 - **erase**: 删除由给定迭代器指向的一个元素或是两个迭代器之间的所有元素。 #### 高效的操作特点 由于内部采用的是双向循环链表形式,因此对于频繁发生的增删动作来说效率很高;但是访问特定索引处的数据则相对较慢,因为这需要遍历整个链条来定位目标节点的位置。 #### 实现细节探讨 在实际应用当中,为了提高性能并减少碎片化现象的发生,某些库可能会对标准模板库(`STL`)的`list`做出改进优化措施。例如,在游戏开发领域广泛使用的`EASTL`(Electronic Arts Standard Template Library),就针对原生`STL`进行了扩展增强处理[^2]。 #### 示例程序展示 下面给出一段简单的例子用于说明如何创建一个整数类型的`list`对象,并对其进行一些基础性的读写操作: ```cpp #include <iostream> #include <list> void printList(const std::list<int>& l) { for (auto it = l.begin(); it != l.end(); ++it) std::cout << *it << ' '; std::cout << '\n'; } int main(){ std::list<int> numbers; // 添加几个数值进入列表 numbers.push_back(789); numbers.push_front(456); numbers.push_back(123); // 打印当前列表状态 printList(numbers); // 对列表执行排序算法 numbers.sort(); printList(numbers); // 清空列表之前先打印其大小 std::cout << "Size before clear(): " << numbers.size() << "\n"; numbers.clear(); // 检查清空后的列表是否为空 if (numbers.empty()) std::cout << "The list is now empty.\n"; return 0; } ``` 此代码片段展示了如何向`list`中加入数据项、调用成员方法获取前后端元素以及利用内置工具完成升序排列等功能。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值