单向链表和C++ std::forward_list详解

文章目录

    • 1. 单向链表和std::forward_list
    • 2. forward_list的用法
      • 2.1 forward_list的定义和声明
      • 2.2 成员函数
        • 2.2.1 基本函数
          • 构造函数
          • 析构函数
          • operator=
          • assign
          • get_allocator
        • 2.2.2 元素访问
          • front
        • 2.2.3 迭代器
          • begin、end和cbegin、cend
          • before_begin, cbefore_begin
        • 2.2.4 容量
          • empty
          • max_size
        • 2.2.5 修改器
          • clear
          • insert_after
          • emplace_after
          • earse_after
          • push_front
          • emplace_front
          • pop_front
          • resize
          • swap
        • 2.2.6 操作
          • merge
          • splice_after
          • remove、remove_if
          • reverse
          • unique
          • sort
      • 2.3 非成员函数
        • operator==,!=,<,<=,>,>=,<=>(std::forward_list)
        • std::swap(std::forward_list)
        • std::erase, std::erase_if (std::forward_list)
    • 3. 总结
      • forward_list容器的优势和劣势:
      • forward_list容器与list的区别

1. 单向链表和std::forward_list

上一章我们介绍了双向链表和C++容器库中提供的std::list容器,与之对应的就是单向链表,顾名思义,单向链表只记录下一个元素的位置,只能朝一个方向遍历元素。C++11从开始提供了std::forward_list(前向列表)来实现单向链表。std::forward_list在插入、删除和移动操作(例如排序)中比其他容器更有用,并且允许时间常数内插入和删除元素。

std::forward_list与std::list不同的是:std::forward_list仅跟踪下一个元素的位置,而std::list同时跟踪下一个和上一个元素,从而增加了存储每个元素所需的存储空间。std::forward_list的缺点是它不能向后迭代,也不能直接访问其各个元素。

2. forward_list的用法

2.1 forward_list的定义和声明

std::forward_list在头文件中定义,其声明如下:

template<
    class T,
    class Allocator = std::allocator<T>
> class forward_list; //C++11 起

namespace pmr {
    template <class T>
    using forward_list = std::forward_list<T, std::pmr::polymorphic_allocator<T>>; //C++17 起
}

其中,参数T为容器要存储的元素类型,对于T需要满足:

  • 要求元素类型是完整类型并满足可擦除。(C++17 前)。
  • 要求元素类型是完整类型并满足可擦除,但许多成员函数附带了更严格的要求。(C++17 起)。

Allocator为用于获取/释放内存及构造/析构内存中元素的分配器。

2.2 成员函数

2.2.1 基本函数

构造函数
  • 功能描述

    • 创建forward_list容器。
  • 函数原型

    //默认构造函数。构造拥有默认构造的分配器的空容器。
    forward_list();
    
    //构造拥有给定分配器 alloc 的空容器。
    explicit forward_list( const Allocator& alloc );
    
    //构造拥有 count 个有值 value 的元素的容器。
    forward_list( size_type count,
                  const T& value,
                  const Allocator& alloc = Allocator()); //C++11 起
    
    //构造拥有 count 个 默认插入的 T 实例的容器。不进行复制。
    explicit forward_list( size_type count ); //C++11 起, C++14 前
    explicit forward_list( size_type count, const Allocator& alloc = Allocator() ); //C++14 起
    
    //构造拥有范围 [first, last) 内容的容器。
    template< class InputIt >
    forward_list( InputIt first, InputIt last,
                  const Allocator& alloc = Allocator() ); //C++11 起
    
    //复制构造函数。构造拥有 other 内容的容器。
    forward_list( const forward_list& other ); //C++11 起
    
    //构造拥有 other 内容的容器,以 alloc 为分配器。
    forward_list( const forward_list& other, const Allocator& alloc ); //C++11 起
    
    //移动构造函数。用移动语义构造拥有 other 内容的容器。分配器通过属于 other 的分配器移动构造获得。
    forward_list( forward_list&& other ); //C++11 起
    
    //有分配器扩展的移动构造函数。以 alloc 为新容器的分配器,从 other 移动内容;如果 alloc != other.get_allocator() ,那么它会导致逐元素移动。
    forward_list( forward_list&& other, const Allocator& alloc ); //C++11 起
    
    //构造拥有初始化器列表 init 内容的容器。
    forward_list( std::initializer_list<T> init,
                  const Allocator& alloc = Allocator() ); //C++11 起
    
    //构造拥有范围 rg 内容的容器。
    template< container-compatible-range<T> R >
    forward_list( std::from_range_t, R&& rg,
                  const Allocator& alloc = Allocator() ); //C++23 起
    
  • 示例

    // C++11 初始化器列表语法:
    std::forward_list<std::string> words1 {"the", "frogurt", "is", "also", "cursed"};
    std::cout << "words1: " << words1 << '\n';
    
    // words2 == words1
    std::forward_list<std::string> words2(words1.begin(), words1.end());
    std::cout << "words2: " << words2 << '\n';
    
    // words3 == words1
    std::forward_list<std::string> words3(words1);
    std::cout << "words3: " << words3 << '\n';
    
    // words4 是 {"Mo", "Mo", "Mo", "Mo", "Mo"}
    std::forward_list<std::string> words4(5, "Mo");
    std::cout << "words4: " << words4 << '\n';
    
    /**************打印结果******************
    words1: [the, frogurt, is, also, cursed]
    words2: [the, frogurt, is, also, cursed]
    words3: [the, frogurt, is, also, cursed]
    words4: [Mo, Mo, Mo, Mo, Mo]
    ***************************************/
    
析构函数
  • 功能描述

    • 销毁 forward_list 。调用元素的析构函数,然后解分配所用的存储。注意,若元素是指针,则不销毁所指向的对象。
  • 函数原型

    ~forward_list(); //C++11 起
    
operator=
  • 功能描述

    • 用于赋值给容器。
  • 函数原型

    //复制赋值运算符。以 other 的副本替换内容。
    forward_list& operator=( const forward_list& other ); //C++11 起
    
    //移动赋值运算符。用移动语义以 other 的内容替换内容
    //即从 other 移动 other 中的数据到此容器中。
    //之后 other 在合法但未指定的状态。
    forward_list& operator=( forward_list&& other ); //C++11 起, C++17 前
    forward_list& operator=( forward_list&& other ) noexcept(); //C++17 起
    
    //以 initializer_list ilist 所标识者替换内容。
    forward_list& operator=( std::initializer_list<T> ilist ); //C++11 起
    
  • 示例

    std::forward_list<int> nums1 {3, 1, 4, 6, 5, 9};
    std::forward_list<int> nums2; 
    std::forward_list<int> nums3;
    
    //从 nums1 复制赋值数据到 nums2
    nums2 = nums1;
    //此时nums2 = {3, 1, 4, 6, 5, 9}
    
    //从 nums1 移动赋值数据到 nums3,
    //修改 nums1 和 nums3
    nums3 = std::move(nums1);
    //此时 nums1 = {}, nums3 = {3, 1, 4, 6, 5, 9}
    
    
    //initializer_list 的复制赋值复制数据给 nums3
    nums3 = {1, 2, 3};
    //此时nums3 = {1, 2, 3}
    
assign
  • 功能描述

    • 将值赋给容器,替换容器的内容。
  • 函数原型

    //以 count 份 value 的副本替换内容。
    void assign( size_type count, const T& value ); C++11 起
    
    //以范围 [first, last) 中元素的副本替换内容。其中有任何一个迭代器是指向 *this 中的迭代器时行为未定义。
    template< class InputIt >
    void assign( InputIt first, InputIt last ); //C++11 起
    
    //以来自 initializer_list ilist 的元素替换内容。
    void assign( std::initializer_list<T> ilist ); //C++11 起
    
  • 示例

    std::forward_list<char> c;
    
    c.assign(5, 'a');//此时c = {'a', 'a', 'a', 'a', 'a'}
    
    const std::string str(6, 'b');
    c.assign(str.begin(), str.end());//此时c = {'b', 'b', 'b', 'b', 'b', 'b'}
    
    c.assign({'C', '+', '+', '1', '1'});//此时c = {'C', '+', '+', '1', '1'}
    
get_allocator
  • 功能描述

    • 返回相关的分配器。
  • 函数原型

//返回值:与容器关联的分配器。
allocator_type get_allocator() const noexcept; //C++11 起

2.2.2 元素访问

front
  • 功能描述

    • 访问容器的第一个元素,其返回值为容器首元素的引用。
  • 函数原型

    reference front(); //C++11 起
    const_reference front() const; //C++11 起
    

    :在空容器上对 front 的调用是未定义的。

2.2.3 迭代器

begin、end和cbegin、cend
  • 功能描述

    • begin和cbegin返回指向forward_list首元素的迭代器,
    • end和cend返回指向forward_list末元素后一元素的迭代器。
  • 函数原型

    iterator begin() noexcept; //C++11 起
    const_iterator begin() const noexcept; //C++11 起
    const_iterator cbegin() const noexcept; //C++11 起
    
    iterator end() noexcept; //C++11 起 
    const_iterator end() const noexcept; //C++11 起
    const_iterator cend() const noexcept; //C++11 起
    

    如果forward_list为空,则返回的迭代器将等于end或cend。end和cend指向forward_list末元素后一元素的迭代器,该元素的表现为占位符,试图访问它将导致未定义行为。

before_begin, cbefore_begin
  • 功能描述

    • 返回指向第一个元素之前迭代器。此元素表现为占位符,试图访问它会导致未定义行为。
  • 函数原型

    iterator before_begin() noexcept; //C++11 起
    const_iterator before_begin() const noexcept; //C++11 起
    const_iterator cbefore_begin() const noexcept; //C++11 起
    

2.2.4 容量

empty
  • 功能描述

    • 检查容器是否为空,若为空则返回true,否则为false。
  • 函数原型

    bool empty() const noexcept; //C++11 起, C++20 前
    [[nodiscard]] bool empty() const noexcept; //C++20 起
    

    其底层实现就是检查容器是否无元素,即判断是否begin() == end()

max_size
  • 功能描述

    • max_size函数返回根据系统或库实现限制的容器可保有的元素最大数量,即对于最大容器std::distance(begin(), end())
  • 函数原型

    size_type max_size() const noexcept; //C++11 起
    

    :此值通常反映容器大小上的理论极限,至多为 std::numeric_limits::max() 。运行时,可用 RAM 总量可能会限制容器大小到小于 max_size() 的值。

2.2.5 修改器

clear
  • 功能描述

    • 擦除所有元素,使用clear()后,再次调用size(),size函数返回0。
  • 函数原型

    void clear() noexcept; //C++11 起
    
insert_after
  • 功能描述

    • 在某个元素后插入新元素,在容器中的指定位置后插入元素。
  • 函数原型

    //在 pos 所指向的元素后插入 value
    //返回值:指向被插入元素的迭代器。
    iterator insert_after( const_iterator pos, const T& value ); //C++11 起
    iterator insert_after( const_iterator pos, T&& value ); //C++11 起
    
    //在 pos 所指向的元素后插入 value 的 count 个副本
    //返回值:指向最后被插入元素的迭代器,或若 count==0 则为 pos 。
    iterator insert_after( const_iterator pos, size_type count, const T& value ); //C++11 起
    
    //在 pos 所指向的元素后插入来自范围 [first, last) 的元素。 若 first 与 last 是指向 *this 中的迭代器则行为未定义。
    //返回值:指向最后被插入元素的迭代器,或若 first==last 则为 pos 。
    template< class InputIt >
    iterator insert_after( const_iterator pos, InputIt first, InputIt last ); //C++11 起
    
    //插入来自 initializer_list ilist 的元素。
    //返回值:指向最后被插入元素的迭代器,或若 ilist 为空则为 pos 。
    iterator insert_after( const_iterator pos, std::initializer_list<T> ilist ); //C++11 起
    
  • 示例

    std::forward_list<std::string> words{"the", "frogurt", "is", "also", "cursed"};
    // words: [the, frogurt, is, also, cursed]
    
    auto beginIt = words.begin();
    words.insert_after(beginIt, "strawberry");
    // words: [the, strawberry, frogurt, is, also, cursed]
    
    auto anotherIt = beginIt;
    ++anotherIt;
    anotherIt = words.insert_after(anotherIt, 2, "strawberry1");
    // words: [the, strawberry, strawberry1, strawberry1, frogurt, is, also, cursed]
    
    std::vector<std::string>
      V = {"apple", "banana", "cherry"};
    anotherIt = words.insert_after(anotherIt, V.begin(), V.end());
    // words: [the, strawberry, strawberry1, strawberry1, apple, banana, cherry, frogurt, is, also, cursed]
    
    words.insert_after(anotherIt, {"jackfruit", "kiwifruit", "lime", "mango"});
    // words: [the, strawberry, strawberry, strawberry, apple, banana, cherry, jackfruit, kiwifruit, lime, mango, frogurt, is, also, cursed]
    
    
emplace_after
  • 功能描述

    • 在元素后原位构造元素。在容器中的指定位置后插入新元素。原位构造元素,即不进行复制或移动操作。准确地以与提供给函数者相同的参数调用元素的构造函数。没有引用和迭代器会失效。
  • 函数原型

    /*----------------------------------
      pos:新元素将构造于其后的迭代器
      args:转发给元素构造函数的参数
      返回值iterator:指向新元素的迭代器
    ------------------------------------*/
    template< class... Args >
    iterator emplace_after( const_iterator pos, Args&&... args ); //C++11 起
    
earse_after
  • 功能描述

    • 擦除元素后的元素.
  • 函数原型

    //移除 pos 后的元素
    //返回值:指向后随被擦除元素的迭代器,或若不存在这种元素则为 end() 
    iterator erase_after( const_iterator pos ); //C++11 起
    
    //移除 first 后 last 前的元素
    //返回值:last
    iterator erase_after( const_iterator first, const_iterator last ); //C++11 起
    
  • 示例

    std::forward_list<int> l = { 1, 2, 3, 4, 5, 6, 7, 8, 9 };
    
    //l.erase( l.begin() ); // 错误:没有名为erase的成员函数
    
    l.erase_after( l.before_begin() ); // 移除首元素
    
    for( auto n : l ) std::cout << n << " ";
    std::cout << '\n';
    //2 3 4 5 6 7 8 9
    
    auto fi= std::next( l.begin() );
    auto la= std::next( fi, 3 );
    
    l.erase_after( fi, la );
    
    for( auto n : l ) std::cout << n << " ";
    std::cout << '\n';
    //2 3 6 7 8 9
    
push_front
  • 功能描述

    • 插入元素到容器起始。前附给定元素 value 到容器起始。没有引用和迭代器会失效。
  • 函数原型

    void push_front( const T& value ); //C++11 起
    void push_front( T&& value ); //C++11 起
    
emplace_front
  • 功能描述

    • 在容器头部原位构造元素。插入新元素到容器起始。
  • 函数原型

    template< class... Args >
    void emplace_front( Args&&... args ); //C++11 起, C++17 前
    
    template< class... Args >
    reference emplace_front( Args&&... args ); //C++17 起
    
pop_front
  • 功能描述

    • 移除容器首元素。若容器中无元素,则行为未定义。指向被擦除元素的迭代器和引用会失效。
  • 函数原型

    void pop_front(); //C++11 起
    
resize
  • 功能描述

    • 改变容器中可存储元素的个数。
  • 函数原型

    //重设容器大小以容纳 count 个元素,在 count == size() 时不做任何事。
    //如果当前大小大于 count,那么减小容器到它的开头 count 个元素。
    //如果当前大小小于 count,那么后附额外的默认插入的元素
    void resize( size_type count );
    
    //重设容器大小以容纳 count 个元素,在 count == size() 时不做任何事。
    //如果当前大小大于 count,那么减小容器到它的开头 count 个元素。
    //如果当前大小小于 count,那么后附额外的 value 的副本
    void resize( size_type count, const value_type& value );
    
swap
  • 功能描述

    • 交换内容。
  • 函数原型

    void swap( forward_list& other ); //C++11 起, C++17 前
    void swap( forward_list& other ) noexcept(); //C++17 起
    

    将内容与 other 的交换。不在单独的元素上调用任何移动、复制或交换操作。所有迭代器和引用保持有效。在操作后,未指明保有此容器中 end() 值的迭代器指代此容器还是另一容器。

2.2.6 操作

merge
  • 功能描述

    • 合并二个已排序列表。
  • 函数原型

    //用 operator< 比较元素
    void merge( forward_list& other ); //C++11 起
    void merge( forward_list&& other ); //C++11 起
    
    //用给定的比较函数 comp
    template < class Compare >
    void merge( forward_list& other, Compare comp ); //C++11 起
    template < class Compare >
    void merge( forward_list&& other, Compare comp ); //C++11 起
    

    如果 other 与 *this 指代同一对象,那么什么也不做。否则,将两个已排序链表归并为一个。链表应以升序排序。不复制元素,并且在操作后容器 other 会变为空。不会无效化任何引用或迭代器,但被移动元素的迭代器现在指代到 *this 中,而不是到 other 中。

    :对于两个链表中的等价元素,来自 *this 的元素始终在来自 other 的元素之前,并且 *this 和 other 的等价元素顺序不更改。如果 get_allocator() != other.get_allocator(),那么行为未定义。

  • 示例

    std::forward_list<int> list1 = {5, 9, 1, 3, 3};
    std::forward_list<int> list2 = {8, 7, 2, 3, 4, 4};
    
    list1.sort(); // 1 3 3 5 9
    list2.sort(); // 2 3 4 4 7 8
    
    list1.merge(list2); // 1 2 3 3 3 4 4 5 7 8 9
    
splice_after
  • 功能描述

    • 从另一 forward_list 移动元素。
  • 函数原型

    /*《参数说明》
    pos	        -	指向将插入内容到其后的元素的迭代器
    other	      -	移动内容来源的另一容器
    it	        -	指向从 other 移动到 *this 的元素的迭代器的前趋迭代器
    first, last	-	从 other 移动到 *this 的元素范围
    */
    
    /*从 other 移动所有元素到 *this 。元素被插入到 pos 所指向的元素后。
    操作后 other 变为空。若 other 与 *this 指代同一对象则行为未定义。*/
    void splice_after( const_iterator pos, forward_list& other ); //C++11 起
    void splice_after( const_iterator pos, forward_list&& other ); //C++11 起
    
    /* 从 other 移动后随 it 的迭代器所指向的元素到 *this 。
    元素被插入到 pos 所指向的元素后,若 pos == it 或若 pos == ++it 则无效果。*/
    void splice_after( const_iterator pos, forward_list& other,
                       const_iterator it ); //C++11 起
    void splice_after( const_iterator pos, forward_list&& other,
                       const_iterator it ); //C++11 起
    
    /*从 other 移动范围 (first, last) 中的元素到 *this 。
    元素被插入到 pos 所指向的元素后。不移动 first 所指向的元素。若 pos 是范围 (first,last) 中的元素则行为未定义。*/
    void splice_after( const_iterator pos, forward_list& other,
                       const_iterator first, const_iterator last ); //C++11 起
    void splice_after( const_iterator pos, forward_list&& other,
                       const_iterator first, const_iterator last ); //C++11 起
    

    不复制元素。 pos 必须是指向 *this 中的可解引用迭代器或 before_begin()迭代器(特别是 end() 不是 pos 的合法参数值)。若 get_allocator() != other.get_allocator() 则行为未定义。没有迭代器或引用被非法化,指向被移动的元素的迭代器现在指代到 *this 中,而非 other 中。

  • 示例

    std::forward_list<int> l1 = {1, 2, 3, 4, 5};
    std::forward_list<int> l2 = {10, 11, 12};
    
    l2.splice_after(l2.cbegin(), l1, l1.cbegin(), l1.cend());
    // 不等价于 l2.splice_after(l2.cbegin(), l1);
    
    for (int n : l1)
      std::cout << n << ' ';
    std::cout << '\n';
    // 1
    
    for (int n : l2)
      std::cout << n << ' ';
    std::cout << '\n';
    //10 2 3 4 5 11 12
    
remove、remove_if
  • 功能描述

    • 移除满足特定标准的元素。
  • 函数原型

    //移除所有等于 value 的元素
    void remove( const T& value ); //C++11 起,C++20 前
    size_type remove( const T& value ); //C++20 起
    
    //移除所有谓词 p 对它返回 true 的元素
    template< class UnaryPredicate >
    void remove_if( UnaryPredicate p ); //C++11 起,C++20 前
    template< class UnaryPredicate >
    size_type remove_if( UnaryPredicate p ); //C++20 起
    
  • 示例

    std::forward_list<int> l = { 1,100,2,3,10,1,11,-1,12 };
    
    l.remove(1); // 移除两个等于 1 的元素
    l.remove_if([](int n){ return n > 10; }); // 移除全部大于 10 的元素
    
    for (int n : l) {
      std::cout << n << ' '; 
    }
    std::cout << '\n';
    //2 3 10 -1
    
reverse
  • 功能描述

    • 将该链表的所有元素的顺序反转。逆转容器中的元素顺序。不非法化任何引用或迭代器。
  • 函数原型

    void reverse() noexcept; //C++11 起
    
  • 示例

    std::forward_list<int> list = { 8,7,5,9,0,1,3,2,6,4 };
    list.reverse(); //4 6 2 3 1 0 9 5 7 8
    
unique
  • 功能描述

    • 删除连续的重复元素。从容器移除所有相继的重复元素。只留下相等元素组中的第一个元素。若选择的比较器不建立等价关系则行为未定义。
  • 函数原型

    //用 operator== 比较元素。
    void unique(); //C++11 起, C++20 前
    
    //用二元谓词 p 比较元素。
    template< class BinaryPredicate >
    void unique( BinaryPredicate p ); //C++11 起, C++20 前
    template< class BinaryPredicate >
    size_type unique( BinaryPredicate p ); //C++20 起
    
  • 示例

    std::forward_list<int> c = {1, 2, 2, 3, 3, 2, 1, 1, 2};
    c.unique(); // 1 2 3 2 1 2
    
    c = {1, 2, 12, 23, 3, 2, 51, 1, 2};
    c.unique([mod = 10](int x, int y)
             { return (x % mod) == (y % mod); });
    // 1 2 23 2 51 2
    
sort
  • 功能描述

    • 对元素进行排序。以升序排序元素。保持相等元素的顺序。
  • 函数原型

    //用 operator< 比较元素
    void sort();  //C++11 起
    	
    //用给定的比较函数 comp, 在第一参数小于(即先序于)第二参数时返回 true。
    template< class Compare >
    void sort( Compare comp );  //C++11 起
    
  • 示例

    std::forward_list<int> list = {8, 7, 5, 9, 0, 1, 3, 2, 6, 4};
    
    list.sort();                    // 0 1 2 3 4 5 6 7 8 9
    list.sort(std::greater<int>()); // 9 8 7 6 5 4 3 2 1 0
    
    list = {1, 2, 14, 25, 3, 22};
    // 按照元素个位数的大小进行排序
    list.sort([mod = 10](int x, int y)
              { return (x % mod) < (y % mod); }); // 1 2 22 3 14 25
    

2.3 非成员函数

operator==,!=,<,<=,>,>=,<=>(std::forward_list)

  • 功能描述

    • 按照字典顺序比较 forward_list 中的值。
  • 函数声明

    //1. ==
    //返回值:在 forward_list 内容相等时返回 true,否则返回 false
    template< class T, class Alloc >
    bool operator==( const std::forward_list<T, Alloc>& lhs,
                     const std::forward_list<T, Alloc>& rhs );
    
    //2. !=
    //返回值:在 forward_list 内容不相等时返回 true,否则返回 false
    template< class T, class Alloc >
    bool operator!=( const std::forward_list<T, Alloc>& lhs,
                     const std::forward_list<T, Alloc>& rhs ); //C++20 前
    
    //3. <
    //返回值:在 lhs 的内容按字典序小于 rhs 的内容时返回 true,否则返回 false
    template< class T, class Alloc >
    bool operator<( const std::forward_list<T, Alloc>& lhs,
                    const std::forward_list<T, Alloc>& rhs ); //C++20 前
    
    //4. <=
    //返回值:在 lhs 的内容按字典序小于或等于 rhs 的内容时返回 true,否则返回 false
    template< class T, class Alloc >
    bool operator<=( const std::forward_list<T, Alloc>& lhs,
                     const std::forward_list<T, Alloc>& rhs ); //C++20 前
    
    //5. >
    //返回值:在 lhs 的内容按字典序大于 rhs 的内容时返回 true,否则返回 false
    template< class T, class Alloc >
    bool operator>( const std::forward_list<T, Alloc>& lhs,
                    const std::forward_list<T, Alloc>& rhs ); //C++20 前
    
    //6. >=
    //返回值:在 lhs 的内容按字典序大于或等于 rhs 的内容时返回 true,否则返回 false
    template< class T, class Alloc >
    bool operator>=( const std::forward_list<T, Alloc>& lhs,
                     const std::forward_list<T, Alloc>& rhs ); //C++20 前
    
    //7. <=>
    //返回值:lhs 与 rhs 中的首对不等价元素的相对顺序,如果有这种元素;否则是 lhs.size() <=> rhs.size()。
    template< class T, class Alloc >
    operator<=>( const std::forward_list<T, Alloc>& lhs,
                                 const std::forward_list<T, Alloc>& rhs ); //C++20 起
    
    • 1,2中会检查 lhs 与 rhs 的内容是否相等,即它们是否拥有相同数量的元素且 lhs 中每个元素与 rhs 的同位置元素比较相等。

    • 3-6中按照字典比较lhs和rhs的内容,其内部等价于调用std::lexicographical_compare函数进行比较。

    • 7中也是按字典序比较lhs和rhs的内容。其内部等价于调用std::lexicographical_compare_three_way 进行比较。返回类型同合成三路比较的结果类型。其逻辑大致如下:

      lhs < rhs ? std::weak_ordering::less :
      rhs < lhs ? std::weak_ordering::greater :
                  std::weak_ordering::equivalent
      //注:通常情况下less对应的是-1,greater对应1,equivalent对应0
      

      lhs与rhs中的首对不等价元素的相对顺序,如果有这种元素;否则是 lhs.size() <=> rhs.size()

  • 示例

    std::forward_list<int> alice{1, 2, 3};
    std::forward_list<int> bob{7, 8, 9, 10};
    std::forward_list<int> eve{1, 2, 3};
    
    std::cout << std::boolalpha;
    
    // 比较不相等的容器
    std::cout << "alice == bob returns " << (alice == bob) << '\n';
    std::cout << "alice != bob returns " << (alice != bob) << '\n';
    std::cout << "alice <  bob returns " << (alice < bob) << '\n';
    std::cout << "alice <= bob returns " << (alice <= bob) << '\n';
    std::cout << "alice >  bob returns " << (alice > bob) << '\n';
    std::cout << "alice >= bob returns " << (alice >= bob) << '\n';
    
    std::cout << '\n';
    
    // 比较相等的容器
    std::cout << "alice == eve returns " << (alice == eve) << '\n';
    std::cout << "alice != eve returns " << (alice != eve) << '\n';
    std::cout << "alice <  eve returns " << (alice < eve) << '\n';
    std::cout << "alice <= eve returns " << (alice <= eve) << '\n';
    std::cout << "alice >  eve returns " << (alice > eve) << '\n';
    std::cout << "alice >= eve returns " << (alice >= eve) << '\n';
    

    输出结果:

    alice == bob returns false
    alice != bob returns true
    alice <  bob returns true
    alice <= bob returns true
    alice >  bob returns false
    alice >= bob returns false
     
    alice == eve returns true
    alice != eve returns false
    alice <  eve returns false
    alice <= eve returns true
    alice >  eve returns false
    alice >= eve returns true
    

std::swap(std::forward_list)

  • 功能描述

    • std::forward_list特化 std::swap算法。
  • 函数原型

    template< class T, class Alloc >
    void swap( std::forward_list<T, Alloc>& lhs,
               std::forward_list<T, Alloc>& rhs ); //C++11 起, C++17 前
    
    template< class T, class Alloc >
    void swap( std::forward_list<T, Alloc>& lhs,
               std::forward_list<T, Alloc>& rhs ) noexcept(); //C++17 起
    

    交换 lhsrhs 的内容。调用lhs.swap(rhs)

  • 示例

    std::forward_list<int> a1{1, 2, 3}, a2{4, 5};
    
    auto it1 = std::next(a1.begin()); //*it1 = 2
    auto it2 = std::next(a2.begin()); //*it2 = 5
    
    int &ref1 = a1.front(); // ref1 = 1
    int &ref2 = a2.front(); // ref1 = 4
    
    std::cout << *it1 << ' ' << *it2 << ' ' << ref1 << ' ' << ref2 << '\n';
    // 打印结果为2 5 1 4
    
    std::swap(a1, a2);
    
    // 此时a1 = {4, 5},a2 = {1, 2, 3}
    std::cout << *it1 << ' ' << *it2 << ' ' << ref1 << ' ' << ref2 << '\n';
    // 打印结果仍为2 5 1 4
    
    /*注:
            交换后迭代器与引用保持与原来的元素关联,
            例如尽管 'a1' 中值为 2 的元素被移动到 'a2' 中,
            原来指向它的 it1 仍指向同一元素。*/
    

std::erase, std::erase_if (std::forward_list)

  • 功能描述

    • 函数主要用来擦除所有满足特定判别标准的元素。
  • 函数原型

    //从容器中擦除所有比较等于value的元素,
    //等价于 return c.remove_if([&](auto& elem) { return elem == value; });
    template< class T, class Alloc, class U >
      typename std::forward_list<T, Alloc>::size_type
        erase(std::forward_list<T, Alloc>& c, const U& value); //C++20 起
    
    //从容器中擦除所有满足pred的元素,pred为应该擦除元素则返回true的一元谓词。
    //等价于 return c.remove_if(pred);
    template< class T, class Alloc, class Pred >
      typename std::forward_list<T, Alloc>::size_type
        erase_if(std::forward_list<T, Alloc>& c, Pred pred); //C++20 起
    

    返回值为被擦除的元素数。

  • 示例

    std::forward_list<int> c{1, 2, 3, 4, 6};
    // 擦除c中的值等于3的元素
    auto erased1 = std::erase(c, 3); // erased1 = 1
    // 此时c = {1, 2, 4, 6}
    
    // 擦除c中的偶数
    auto erased2 = std::erase_if(c, [](int n)
                                 { return n % 2 == 0; }); // erased2 = 3
    // 此时c = {1}
    

3. 总结

forward_list容器的优势和劣势:

优势

  • 采用动态内存分配,不会造成内存浪费和溢出。且使用的内存比list小。
  • 重组操作不需要移动/复制元素(适用于存储具有高复制/大分配成本的对象)。

劣势

  • 仅在线性时间内随机访问。
  • 只能单向遍历。
  • 有时候可能会由于内存局部性错误而导致遍历缓慢。

forward_list容器与list的区别

forward_list list
使用单向链表实现 使用双向链表实现
消耗相对较少的内存 消耗相对更多的内存
由于每个节点的指针较少,因此插入和移除元素的开销更少,因此性能更好。 由于每个节点的指针更多,插入和删除元素的开销更大,因此性能较差。
正向顺序访问 正向和反向顺序访问
比list更有效。 效率低于forward_list表。
通常用于需要单向顺序访问的情况,例如实现二叉树、哈希表、堆栈等。 通常用于需要双向顺序访问的情况,例如在散列中实现链接、图的邻接列表表示等。

文章首发公众号:iDoitnow如果喜欢话,可以关注一下

你可能感兴趣的:(C++,链表,c++,list,forward_list,前向链表,容器)