std::list

      
      
      
      
  1. //
  2. template < class T, class Alloc = allocator<T> > class list;

列表(List)是一个允许在序列中任何一处位置以常量耗时插入或删除元素且可以双向迭代的顺序容器(Sequence container)。

A list is a sequence container that supports bidirectional iterators and allows constant time insert and erase operations anywhere within the sequence, with storage management handled automatically.

C++编程语言国际标准:ISO/IEC 14882:2011

容器特性

(1)顺序序列

顺序容器中的元素按照严格的线性顺序排序。可以通过元素在序列中的位置访问对应的元素。

(2)双向链表

容器中的每个元素保存了定位前一个元素及后一个元素的信息,允许在任何一处位置以常量耗时进行插入或删除操作,但不能进行直接随机访问(Direct random access)。

(3)能够感知内存分配器的(Allocator-aware)

容器使用一个内存分配器对象来动态地处理它的存储需求。

  • 模板参数

    T

    存储在容器中的元素的数据类型。

    在类模板内部,使用其别名为 value_type 的成员类型。

    Alloc

    容器内部用来管理内存分配及释放的内存分配器的类型。

    这个参数是可选的,它的默认值是 std::allocator,这个是一个最简单的非值依赖的(Value-independent)内存分配器。 在类模板内部,使用其别名为 allocator_type 的成员类型。


  • 详细说明


                                                                                                       标准模板库(Standard Template Library,简称STL)中的列表容器采用的是双向链表(Doubly-linked lists)数据结构。双向链表可以保存以不同且无关的储存位置容纳的元素。元素间的顺序是通过各个元素中指向前一个元素的链接及指向后一个元素的链接等联系维护的。

    C++11 该特点跟 std:: forward_list 容器的有点相似:主要的区别就是 std:: forward_list 是一个单向链表,因此它只能被正向迭代( Iterated forward),以换取更小更高效的特点。

    增加或移动列表中的元素不会使指向它们的指针、引用、迭代器失效,只有当对应的元素被销毁时才会如此。

    你可能会好奇,为什么STL同时提供列表(std::list)跟向量(std::vector)及其它等多种容器,原因是它们的底层实现方式是不一样的,每一种实现方式都有各自的消耗(空间或时间)及优点。

    相比较于其它的基本标准顺序容器(数组 std::array、向量 std::vector、双端队列 std::deque 等),列表通常在容器中已获得迭代器的任意位置处插入、获得(Extracting,提取)、移动元素等操作中表现出更出色的性能,对那些密集使用上述操作的算法,使用列表同样能提升性能,比如排序算法。

    Since lists allow fast insertion and erasing from the middle of a list, certain operations are provided specifically for them.

    C++编程语言国际标准:ISO/IEC 14882:2011

    双向链表(std::list)及正向链表(std:: forward_list C++11)相比于其它顺序容器的主要缺点是它们不能够通过元素在容器中的位置直接访问(Direct access)元素。举个例子:如果想访问一个列表中的第六个元素,那么就必须从一个已知位置(比如开头或末尾)处开始迭代,这会消耗与两个位置之间距间相关的线性时间。而且它们还为保存各个元素间的链接信息消耗更多额外的内存(这点对由小尺寸元素组成的大列表尤为明显)。

    在定义一个列表对象时,所有你需要做的就是给出保存在列表中的元素的类型(更多定义方式可以参考 list::list 成员函数)。举个例子,下述代码定义了一个整数列表:

          
          
          
          
    1. std::list integer_list;

    模板类中提供了成员函数 push_front 及 push_back,你可以分别用它们在列表头或尾插入一个新的元素。举个例子:

          
          
          
          
    1. std::list integer_list;
    2.  
    3. integer_list.push_front(1);
    4. integer_list.push_front(2);

    这个例子创建了一个列表,其中有两个元素,元素 1 及之后的元素 2。

    从 C++11 开始,你也可以使用 emplace_front 及 emplace_back 分别向列表头或尾插入一个新的元素。

    相对地,你也可以通过使用模板类提供的 pop_front 及 pop_back 成员函数来分别删除第一个或最后一个元素。单独地使用其中的部份函数,就能很容易的创建基于列表的队列(Queue)或栈(Stack)等数据结构,当然,最好还是使用由标准模板库提供的容器适配器 std::stack 及 std::deque

    快速的在容器中间部份插入元素是列表的最大优势。可以使用成员函数 insert 完成这一操作:需要指定插入的元素及指向插入位置的迭代器(新的元素将会插入到当前所指向元素的前面)。从 C++11 开始,你也可以使用 emplace 来完成这一操作,具体区别请查看两个函数的详细介绍。以下是 list::insert 的声明:

          
          
          
          
    1. iterator insert(iterator position, const T& element_to_insert);

    其中迭代器可以按以下方式定义:

          
          
          
          
    1. list::iterator iterator_name; // type要替换成元素的具体类型

    所要注意的是标准模板库中的列表同时支持正向(Forward)及反向(Reverse)迭代(因为它是基于双链表实现的)。

    使用 insert 及 end 函数可以实现 push_back (及 push_front)的功能:

          
          
          
          
    1. std::list integer_list;
    2. integer_list.insert(integer_list.end(), item);

    正如所料的,list 类模板包含了由标准容器定义的 size 及 empty 函数。这里有一点必须要注意:size 函数的时间复杂度为O(n)(从 C++11 开始,时间复杂度变为 O(1))。所以,如果你想简单判断一个列表是否为空,可以使用 empty 而不是 检查它的大小是否为 0。如果希望保证当前列表是空的,可以使用 clear 函数。具体例子如下:

          
          
          
          
    1. std::list foo;
    2. // 会让人困惑,即使C++11有所改进
    3. if(foo.size() == 0)
    4. ...
    5. // 推荐的方法
    6. if(foo.empty())
    7. ...
    8.  
    9. foo.clear();
    10. // 现在,foo 是一个空列表

    可以使用 sort 成员函数对列表进行排序,该操作时间复杂度保证为 O(nlogn)

    注意,标准库中提供的  std::sort 排序函数需要容器支持随机访问迭代器,而  list 模板类并未提供该支持(直接原因是  list 提供的迭代器不支持  operator- 操作符函数),所以  std::sort 不支持对  list 的排序。

    sort 函数的代码示例:

          
          
          
          
    1. foo.sort();

    可以使用 reverse 成员函数来反转列表。标准C++库的算法库中存在一个相似的函数:std::reverse,该函数同样用于反转容器中的元素。当前成员函数(list::reverse)与 std::reverse 的区别就是调用 list::reverse 后不会影响其它正在使用的迭代器所指向的值。

    reverse 成员函数的代码示例:

          
          
          
          
    1. foo.reverse()

    可以使用 unique 成员函数来“唯一化”元素。该函数删除容器中的所有连续重复元素,仅仅留下每组等值元素中的第一个元素。比如,有如下元素组成列表:

    1 1 8 9 7 8 2 3 3

    在调用 unique 后,剩余的元素为:

    1 8 9 7 8  2 3

    注意上述结果中,仍然有两个 8:仅仅删除连续的等值元素。如果希望一个元素在序列中仅仅出现一次,你需要先排序这个列表。具体例子如下:

          
          
          
          
    1. std::list int_list;
    2. int_list.push_back(1);
    3. int_list.push_back(1);
    4. int_list.push_back(8);
    5. int_list.push_back(9);
    6. int_list.push_back(7);
    7. int_list.push_back(8);
    8. int_list.push_back(2);
    9. int_list.push_back(3);
    10. int_list.push_back(3);
    11. int_list.sort();
    12. int_list.unique();
    13.  
    14. for(std::list::iterator list_iter = int_list.begin();
    15. list_iter != int_list.end(); list_iter++)
    16. {
    17. std::cout<<*list_iter<<endl;
    18. }
  • 成员类型

    成员类型 定义
    value_type 第一个模板参数 T
    allocator_type 第二个模板参数 Alloc
    size_type 无符号整数类型(通常为 size_t
    difference_type 有符号整数类型(通常为 ptrdiff_t
    reference Allocator::reference 已弃用
    value_type& C++11
    const_reference Allocator::const_reference 已弃用
    const value_type& C++11
    pointer Allocator::pointer 已弃用
    std::allocator_traits::pointer C++11
    const_pointer Allocator::const_pointer 已弃用
    std::allocator_traits::const_pointer C++11
    iterator 双向迭代器
    const_iterator 常双向迭代器
    reverse_iterator std::reverse_iterator
    const_reverse_iterator std::reverse_iterator
  • 成员函数


    (constructor) 创建 list
    (destructor) 释放 list
    operator= 值赋操作

    Iterators:

    begin 返回指向容器起始位置的迭代器(iterator
    end 返回指向容器末尾位置的迭代器
    rbegin 返回指向容器逆序起始位置的逆序迭代器(reverse_iterator
    rend 返回指向容器逆序末尾位置的逆序迭代器
    cbegin C++11 返回指向容器起始位置的常迭代器(const_iterator
    cend C++11 返回指向容器末尾位置的常迭代器
    crbegin C++11 返回指向容器逆序起始位置的常逆序迭代器(const_reverse_iterator
    crend C++11 返回指向容器逆序末尾位置的常逆序迭代器

    Capacity:

    empty 判断是否为空
    size 返回有效元素个数
    max_size 返回 list 支持的最大元素个数

    Element access:

    front 访问第一个元素
    back 访问最后一个元素

    Modifiers

    assign 将新的内容赋值给容器
    emplace_front C++11  在容器开头构造及插入一个元素
    push_front 在容器开头插入一个元素
    pop_front 删除第一个元素
    emplace_back C++11 在容器末尾构造及插入一个元素
    push_back 在末尾增加一个元素
    pop_back 删除最后一个元素
    emplace C++11 构造及插入一个元素
    insert 插入元素
    erase 删除元素
    swap 交换内容
    resize 改变有效元素的个数
    clear 清空内容

    Operations:

    splice 使元素从一个列表移动到另一个列表
    remove 删除值为指定值的元素
    remove_if 删除满足指定条件的元素
    unique 删除重复值
    merge 合并已排序的列表
    sort 为容器中的所有元素排序
    reverse 反转元素的顺序

    Observers

    get_allocator 获得内存分配器
  • 非成员函数

    operator==、operator!=、operator<、operator<=、operator>、operator>= 关系操作符
    std::swap 交换两个列表的内容
  • 算法相关

                                                                                                            头文件中包含大量与 list 容器相关的算法,常使用的有

    搜索算法 std::adjacent_findstd::count、
    std::count_ifstd::find、
    std::find_ifstd::find_end、
    std::find_first_ofstd::search、
    std::search_nstd::equal、
    std::mismatch
    二分查找(Binary search) std::lower_boundstd::upper_bound、
    std::equal_rangestd::binary_search
    集合(Set)操作 std::includesstd::set_difference、
    std::set_intersectionstd::set_union、
    std::set_symmetric_difference
    最大与最小 std::min_elementstd::max_element
    字典序比较 std::lexicographical_compare
    排列生成器 std::next_permutation
    std::prev_permutation
    其它操作 std::all_ofstd::any_ofstd::none_of
    std::for_eachstd::copystd::copy_if
    std::copy_nstd::copy_backward
    ​std::movestd::move_backward
    std::swap_rangesstd::iter_swap
    std::transformstd::replace
    std::replace_ifstd::replace_copy
    std::replace_copy_ifstd::fill
    std::fill_nstd::generate
    std::generate_nstd::remove
    std::remove_ifstd::unique
    std::unique_copystd::reverse
    ​std::reverse_copystd::rotate
    std::rotate_copystd::random_shuffle
    std::shufflestd::partition
    std::is_partitionedstd::stable_partition
    std::partition_copystd::merge

    具体内容详见各个功能函数。除了排序(std::sort 等)、堆操作(std::make_heapstd::sort_heap 等)、第n个元素(std::nth_element),适用于 std::vector 的例子基本上都适用于 std::list,可以参考 vector 与算法 主题,该主题包含大量的代码示例。

  • 代码示例


    注意!以下代码仅用于快速了解 list,具体代码示例请查看各个成员函数或上章节提到的 vector 与算法 主题。

    综合

    下面这个例子简单地介绍了 list 的常用使用方法。

          
          
          
          
    1. #include
    2. #include
    3. #include
    4. #include
    5.  
    6. namespace ClassFoo{
    7.  
    8. using namespace std;
    9.  
    10. //创建一个list容器的实例LISTINT
    11. typedef list LISTINT;
    12.  
    13. //创建一个list容器的实例LISTCHAR
    14. typedef list LISTCHAR;
    15.  
    16. void ListExample1(){
    17. //--------------------------
    18. //用list容器处理整型数据
    19. //--------------------------
    20. //用LISTINT创建一个名为foo1的list对象
    21. LISTINT foo1;
    22. //声明i为迭代器
    23. LISTINT::iterator i;
    24. //从前面向foo1容器中添加数据
    25. foo1.push_front (2);
    26. foo1.push_front (1);
    27. //从后面向foo1容器中添加数据
    28. foo1.push_back (3);
    29. foo1.push_back (4);
    30. //从前向后显示foo1中的数据
    31. cout<<"foo1.begin()--- foo1.end():"<<endl;
    32. for (i = foo1.begin(); i != foo1.end(); ++i)
    33. cout << *i << " ";
    34. cout << endl;
    35. //从后向后显示foo1中的数据
    36. LISTINT::reverse_iterator ir;
    37. cout<<"foo1.rbegin()---foo1.rend():"<<endl;
    38. for (ir =foo1.rbegin(); ir!=foo1.rend();ir++) {
    39. cout << *ir << " ";
    40. }
    41. cout << endl;
    42. //使用STL的accumulate(累加)算法
    43. int result = accumulate(foo1.begin(), foo1.end(),0);
    44. cout<<"Sum="<<result<<endl;
    45. cout<<"------------------"<<endl;
    46. //--------------------------
    47. //用list容器处理字符型数据
    48. //--------------------------
    49. //用LISTCHAR创建一个名为foo1的list对象
    50. LISTCHAR foo2;
    51. //声明i为迭代器
    52. LISTCHAR::iterator j;
    53. //从前面向foo2容器中添加数据
    54. foo2.push_front ('A');
    55. foo2.push_front ('B');
    56. //从后面向foo2容器中添加数据
    57. foo2.push_back ('x');
    58. foo2.push_back ('y');
    59. //从前向后显示foo2中的数据
    60. cout<<"foo2.begin()---foo2.end():"<<endl;
    61. for (j = foo2.begin(); j != foo2.end(); ++j)
    62. cout << char(*j) << " ";
    63. cout << endl;
    64. //使用STL的max_element算法求foo2中的最大元素并显示
    65. j=max_element(foo2.begin(),foo2.end());
    66. cout << "The maximum element in foo2 is: "<<char(*j)<<endl;
    67. }
    68. }
    69.  
    70. int main()
    71. {
    72. ClassFoo::ListExample1();
    73. return 0;
    74. }

    输出

    foo1.begin()--- foo1.end():
    1 2 3 4
    foo1.rbegin()---foo1.rend():
    4 3 2 1
    Sum=10
    ------------------
    foo2.begin()---foo2.end():
    B A x y
    The maximum element in foo2 is: y

    用 list 实现的简单的对象池

          
          
          
          
    1. #include
    2. #include
    3.  
    4. namespace ClassFoo{
    5.  
    6. using namespace std;
    7.  
    8. template<class T>
    9. class object_pool
    10. {
    11. list<void *> data_list;
    12. public:
    13. void* malloc_data()
    14. {
    15. if(!data_list.empty())
    16. {
    17. list<void *>::iterator it = data_list.begin();
    18. void *p = *it;
    19. data_list.pop_front();
    20. return p;
    21. }
    22. else
    23. {
    24. void* p = malloc(sizeof(T));
    25. return p;
    26. }
    27. }
    28.  
    29. void free_data(void* p)
    30. {
    31. data_list.push_back(p);
    32. }
    33. };
    34.  
    35. class A
    36. {
    37. public:
    38. int value;
    39. A(int a):value(a){cout<<"A("<<a<<") called"<<endl;}
    40. ~A() {cout<<"~A("<<value<<") called"<<endl;}
    41.  
    42. static object_pool<A> pool_;
    43. void* operator new (size_t size)
    44. {
    45. return pool_.malloc_data();
    46. }
    47.  
    48. void operator delete(void* p)
    49. {
    50. pool_.free_data(p);
    51. }
    52.  
    53. };
    54.  
    55. object_pool<A> A::pool_;
    56.  
    57. void ListExample2() {
    58. A* a1=new A(1);
    59. A* a2=new A(2);
    60. delete a1;
    61. delete a2;
    62. }
    63. }
    64. void main()
    65. {
    66. ClassFoo::ListExample2();
    67. }

    输出

    A(1) called
    A(2) called
    ~A(1) called
    ~A(2) called

    排序自定义结构体对象列表

          
          
          
          
    1. #include
    2. #include
    3. #include
    4.  
    5. #define CLASSFOO_LIST(type, name, ...) \
    6. static const type name##_a[] = __VA_ARGS__; \
    7. std::list name(name##_a, name##_a + sizeof(name##_a) / sizeof(*name##_a))
    8.  
    9. namespace ClassFoo{
    10. struct ClassFoo {
    11. int i;
    12. std::string s;
    13. };
    14. bool compare(ClassFoo first, ClassFoo second)
    15. {
    16. if (first.i < second.i)
    17. return true;
    18. else
    19. return false;
    20. }
    21. void ListExample3(){
    22. CLASSFOO_LIST(ClassFoo,foo,{{2,"我"},{1,"你"},{3,"他"}});
    23. std::list<ClassFoo>::iterator it;
    24. foo.sort(compare);
    25. for (it = foo.begin(); it != foo.end(); it++ )
    26. std::cout << it->i << " " << it->s << std::endl;
    27. }
    28. }
    29. int main()
    30. {
    31. ClassFoo::ListExample3();
    32. return 0;
    33. }

    输出

    1 你
    2 我
    3 他

    一个有用的宏

    类似 C++11 标准中新增的初始化列表构造函数。

          
          
          
          
    1. #define CLASSFOO_LIST(type, name, ...) \
    2. static const type name##_a[] = __VA_ARGS__; \
    3. std::list name(name##_a, name##_a + sizeof(name##_a) / sizeof(*name##_a))

    可以这样使用:

          
          
          
          
    1. CLASSFOO_LIST(int,foo,{1,2,3,4,5,6,7});

你可能感兴趣的:(c++学习)