C++ STL list容器(深入了解,一文学会)

        list 容器,又称双向链表容器,即该容器的底层是以双向链表的形式实现的。这意味着,list 容器中的元素可以分散存储在内存空间里,而不是必须存储在一整块连续的内存空间中。

        list 容器中各个元素的前后顺序是靠指针来维系的,每个元素都配备了 2 个指针,分别指向它的前一个元素和后一个元素。其中第一个元素的前向指针总为 null,因为它前面没有元素;同样,尾部元素的后向指针也总为 null。

        基于这样的存储结构,list 容器具有一些其它容器(array、vector 和 deque)所不具备的优势,即它可以在序列已知的任何位置快速插入或删除元素(时间复杂度为O(1))。并且在 list 容器中移动元素,也比其它容器的效率高。

        使用 list 容器的缺点是,它不能像 array 和 vector 那样,通过位置直接访问元素。举个例子,如果要访问 list 容器中的第 6 个元素,它不支持容器对象名[6]这种语法格式,正确的做法是从容器中第一个元素或最后一个元素开始遍历容器,直到找到该位置。

实际场景中,如何需要对序列进行大量添加或删除元素的操作,而直接访问元素的需求却很少,这种情况建议使用 list 容器存储序列。

  本文作者原创,转载请附上文章出处与本文链接。

C++ STL list容器(深入了解,一文学会)目录

1 list容器的成员函数

2 list容器特点

3 list容器初始化

4 使用方法

4.1 list insert()

4.2 list delete

5 list容器底层实现

 5.1 list 容器节点结构

5.2 list容器迭代器的底层实现

5.3 list容器的底层实现


1 list容器的成员函数

成员函数功能
begin()返回指向容器中第一个元素的双向迭代器。
end()返回指向容器中最后一个元素所在位置的下一个位置的双向迭代器。
rbegin()返回指向最后一个元素的反向双向迭代器。
rend()返回指向第一个元素所在位置前一个位置的反向双向迭代器。
cbegin()和 begin() 功能相同,只不过在其基础上,增加了 const 属性,不能用于修改元素。
cend()和 end() 功能相同,只不过在其基础上,增加了 const 属性,不能用于修改元素。
crbegin() 和 rbegin() 功能相同,只不过在其基础上,增加了 const 属性,不能用于修改元素。
crend()和 rend() 功能相同,只不过在其基础上,增加了 const 属性,不能用于修改元素。
empty()判断容器中是否有元素,若无元素,则返回 true;反之,返回 false。
size()返回当前容器实际包含的元素个数。
max_size()返回容器所能包含元素个数的最大值。这通常是一个很大的值,一般是 232-1,所以我们很少会用到这个函数。
front()返回第一个元素的引用。
back()返回最后一个元素的引用。
assign()用新元素替换容器中原有内容。
emplace_front()在容器头部生成一个元素。该函数和 push_front() 的功能相同,但效率更高。
push_front()在容器头部插入一个元素。
pop_front()删除容器头部的一个元素。
emplace_back()在容器尾部直接生成一个元素。该函数和 push_back() 的功能相同,但效率更高。
push_back()在容器尾部插入一个元素。
pop_back()删除容器尾部的一个元素。
emplace()在容器中的指定位置插入元素。该函数和 insert() 功能相同,但效率更高。
insert() 在容器中的指定位置插入元素。
erase()删除容器中一个或某区域内的元素。
swap()交换两个容器中的元素,必须保证这两个容器中存储的元素类型是相同的。
resize()调整容器的大小。
clear()删除容器存储的所有元素。
splice()将一个 list 容器中的元素插入到另一个容器的指定位置。
remove(val)删除容器中所有等于 val 的元素。
remove_if()删除容器中满足条件的元素。
unique()删除容器中相邻的重复元素,只保留一个。
merge()合并两个事先已排好序的 list 容器,并且合并之后的 list 容器依然是有序的。
sort()通过更改容器中元素的位置,将它们进行排序。
reverse()反转容器中元素的顺序

2 list容器特点

只有运用迭代器,才能访问 list 容器中存储的各个元素。list 模板类提供了如下所示的这些迭代器函数:

迭代器函数功能
begin()返回指向容器中第一个元素的双向迭代器(正向迭代器)。
end()返回指向容器中最后一个元素所在位置的下一个位置的双向迭代器。(正向迭代器)。
rbegin() 返回指向最后一个元素的反向双向迭代器。
rend()返回指向第一个元素所在位置前一个位置的反向双向迭代器。
cbegin()和 begin() 功能相同,只不过在其基础上,正向迭代器增加了 const 属性,即不能用于修改元素。
cend()和 end() 功能相同,只不过在其基础上,正向迭代器增加了 const 属性,即不能用于修改元素。
crbegin()和 rbegin() 功能相同,只不过在其基础上,反向迭代器增加了 const 属性,即不能用于修改元素。
crend()

和 rend() 功能相同,只不过在其基础上,反向迭代器增加了 const 属性,即不能用于修改元素

除此之外,C++ 11 新添加的 begin() 和 end() 全局函数也同样适用于 list 容器。即当操作对象为 list 容器时,其功能和 begin()、end() 成员函数相同。

 注意,list 容器的底层实现结构为双向链表,上图 这种表示仅是为了方便理解各个迭代器函数的功能。

上图可以看出,这些成员函数通常是成对使用的,即 begin()/end()、rbegin()/rend()、cbegin()/cend()、crbegin()/crend() 各自成对搭配使用。其中,begin()/end() 和 cbegin/cend() 的功能是类似的,同样 rbegin()/rend() 和 crbegin()/crend() 的功能是类似的。

list 容器迭代器最大的不同在于,其配备的迭代器类型为双向迭代器,而不再是随机访问迭代器。

这意味着,假设 p1 和 p2 都是双向迭代器,则它们支持使用 ++p1、 p1++、 p1--、 p1++、 *p1、 p1==p2 以及 p1!=p2 运算符,但不支持以下操作(其中 i 为整数):

  • p1[i]:不能通过下标访问 list 容器中指定位置处的元素。
  • p1-=i、 p1+=i、 p1+i 、p1-i:双向迭代器 p1 不支持使用 -=、+=、+、- 运算符。
  • p1<p2、 p1>p2、 p1<=p2、 p1>=p2:双向迭代器 p1、p2 不支持使用 <、 >、 <=、 >= 比较运算符。

3 list容器初始化

#include <list>

//创建一个没有任何元素的空 list 容器: 
list<int> values;

//创建一个包含 n 个元素的 list 容器: 
list<int> values_A(10);

//创建一个包含 n 个元素的 list 容器,并为每个元素指定初始值。例如: 
//创建了一个包含 10 个元素并且值都为 5 个 values 容器。
list<int> values_B(10, 5);

//在已有 list 容器的情况下,通过拷贝该容器可以创建新的 list 容器。例如:
//采用此方式,必须保证新旧容器存储的元素类型一致。
list<int> value1(10);
list<int> value2(value1);

//通过拷贝其他类型容器(或者普通数组)中指定区域内的元素,可以创建新的 list 容器。例如: 
	//拷贝普通数组,创建list容器
int a[] = { 1,2,3,4,5 };
list<int> values_D(a, a + 5);
//拷贝其它类型的容器,创建 list 容器
list<int>arr{ 11,12,13,14,15 };
list<int>values_E(arr.begin(), arr.end());

4 使用方法

	//创建空的 list 容器
	list<double> values;
	//向容器中添加元素
	values.push_back(9);
	values.push_back(8);
	values.push_back(10);
	values.push_back(6);
	values.push_back(5);
	values.push_back(4);
	values.push_back(1);
	values.push_back(2);
	values.push_back(3);
	values.push_back(7);


	int inta = values.size();
	//对容器中的元素进行排序
	values.sort();
	//使用迭代器输出list容器中的元素
	for (list<double>::iterator it = values.begin(); it != values.end(); ++it) {
		*it;
	}

	//使用begin()/end()迭代器函数对输出list容器中的元素
	for (list<double>::iterator it = values.begin(); it != values.end(); ++it) {
		*it;
	}

	//使用 rbegin()/rend()迭代器函数输出 lsit 容器中的元素
	for (list<double>::reverse_iterator it = values.rbegin(); it != values.rend(); ++it) {
		*it;
	}

	//创建 begin 和 end 迭代器
	list<double>::iterator begin = values.begin();
	list<double>::iterator end = values.end();
	//头部和尾部插入字符 '1'
	values.insert(begin, '1');
	values.insert(end, '1');
	while (begin != end)
	{
		*begin;
		++begin;
	}

	//通过 front() 和 back() 成员函数,可以分别获得 list 容器中第一个元素和最后一个元素的引用形式。
	//通过 front() 和 back() 的返回值,我们不仅能分别获取当前 list 容器中的首尾元素,必要时还能修改它们的值。
	std::list<int> mylist{ 1,2,3,4 };
	int &first = mylist.front();
	int &last = mylist.back();
	first; 
	last;

	first = 10;
	last = 20;
	mylist.front();
	mylist.back();

	//对于非 const 类型的 list 容器,迭代器不仅可以访问容器中的元素,也可以对指定元素的值进行修改。
	//如果想访问 list 容存储的其他元素,就只能使用 list 容器的迭代器。例如:
	const std::list<int> mylist{ 1,2,3,4,5 };
	auto it = mylist.begin();
	*it;
	++it;
	while (it != mylist.end())
	{
		*it;
		++it;
	}

4.1 list insert()

语法格式用法说明
iterator insert(pos,elem)在迭代器 pos 指定的位置之前插入一个新元素 elem,并返回表示新插入元素位置的迭代器。
iterator insert(pos,n,elem)在迭代器 pos 指定的位置之前插入 n 个元素 elem,并返回表示第一个新插入元素位置的迭代器。
iterator insert(pos,first,last) 在迭代器 pos 指定的位置之前,插入其他容器(例如 array、vector、deque 等)中位于 [first,last) 区域的所有元素,并返回表示第一个新插入元素位置的迭代器。
iterator insert(pos,initlist)在迭代器 pos 指定的位置之前,插入初始化列表(用大括号 { } 括起来的多个元素,中间有逗号隔开)中所有的元素,并返回表示第一个新插入元素位置的迭代器。

    #include <iostream>
    #include <list>
    #include <array>
    using namespace std;
    int main()
    {
        std::list<int> values{ 1,2 };
        //第一种格式用法
        values.insert(values.begin() , 3);//{3,1,2}
        //第二种格式用法
        values.insert(values.end(), 2, 5);//{3,1,2,5,5}
        //第三种格式用法
        std::array<int, 3>test{ 7,8,9 };
        values.insert(values.end(), test.begin(), test.end());//{3,1,2,5,5,7,8,9}
        //第四种格式用法
        values.insert(values.end(), { 10,11 });//{3,1,2,5,5,7,8,9,10,11}
        for (auto p = values.begin(); p != values.end(); ++p)
        {
            cout << *p << " ";
        }
        return 0;
    }

    //输出结果为: 3 1 2 5 5 7 8 9 10 11

    vector<int>v_int;

    CString strText = "";
    int		intSize = 0;

    // push_back(elem)在数组最后添加数据 
    for (int i = 0; i < 10; i++) 
    {
	v_int.push_back(i);
    }

    //insert():插入元素到Vector中
    v_int.insert(v_int.begin() + 3, 100);	//将 10 插入到下标为3的位置 

4.2 list delete

成员函数功能
pop_front()删除位于 list 容器头部的一个元素。
pop_back()删除位于 list 容器尾部的一个元素。
erase()该成员函数既可以删除 list 容器中指定位置处的元素,也可以删除容器中某个区域内的多个元素。
clear()删除 list 容器存储的所有元素。
remove(val)删除容器中所有等于 val 的元素。
unique()删除容器中相邻的重复元素,只保留一份。
remove_if()删除容器中满足条件的元素。

	list<int>values{ 1,2,3,4,5,6,7,8 };

	//删除当前容器中首个元素
	values.pop_front();//{2,3,4}

	//删除当前容器最后一个元素
	values.pop_back();//{2,3}

	//删除当前容器某一个位置的元素
	//auto first = values.begin();
	//first++;
	values.erase(++values.begin()); //{1,3,4,5}

	//删除当前容器 X下标 -  Y下标 的内容
	auto first = values.begin();
	auto last = values.end();
	first++; last--;
	values.erase(first, last);

	for (auto begin = values.begin(); begin != values.end(); ++begin)
	{
		cout << *begin << " ";
	}

	//清空容器,删除容器中所有的元素
	values.clear(); //{}

5 list容器底层实现

        容器的底层是用双向链表实现的,甚至一些 STL 版本中(比如 SGI STL),list 容器的底层实现使用的是双向循环链表。

        头指针,使用链表存储数据,并不会将它们存储到一整块连续的内存空间中。恰恰相反,各元素占用的存储空间(又称为节点)是独立的、分散的,它们之间的线5.1 性关系通过指针来维持。

 5.1 list 容器节点结构

双向链表的各个节点中存储的不仅仅是元素的值,还应包含 2 个指针,分别指向前一个元素和后一个元素。

    template<typename T,...>
    struct __List_node{
        //...
        __list_node<T>* prev;
        __list_node<T>* next;
        T myval;
        //...
    }

5.2 list容器迭代器的底层实现

        和 array、vector 这些容器迭代器的实现方式不同,由于 list 容器的元素并不是连续存储的,所以该容器迭代器中,必须包含一个可以指向 list 容器的指针,并且该指针还可以借助重载的 *、++、--、==、!= 等运算符,实现迭代器正确的递增、递减、取值等操作。

        可以看到,迭代器的移动就是通过操作节点的指针实现的。

    template<tyepname T,...>
    struct __list_iterator{
        __list_node<T>* node;
        //...
        //重载 == 运算符
        bool operator==(const __list_iterator& x){return node == x.node;}
        //重载 != 运算符
        bool operator!=(const __list_iterator& x){return node != x.node;}
        //重载 * 运算符,返回引用类型
        T* operator *() const {return *(node).myval;}
        //重载前置 ++ 运算符
        __list_iterator<T>& operator ++(){
            node = (*node).next;
            return *this;
        }
        //重载后置 ++ 运算符
        __list_iterator<T>& operator ++(int){
            __list_iterator<T> tmp = *this;
            ++(*this);
            return tmp;
        }
        //重载前置 -- 运算符
        __list_iterator<T>& operator--(){
            node = (*node).prev;
            return *this;
        }
        //重载后置 -- 运算符
        __list_iterator<T> operator--(int){
            __list_iterator<T> tmp = *this;
            --(*this);
            return tmp;
        }
        //...
    }

5.3 list容器的底层实现

        list 容器实际上就是一个带有头节点的双向循环链表。

以下博客部分内容借鉴自:http://c.biancheng.net/stl/。

C++ STL 容器、迭代器、适配器(深入了解,一文学会)    https://blog.csdn.net/qq_37529913/article/details/120052137                                                                                C++ STL deque容器(深入了解,一文学会)                       https://blog.csdn.net/qq_37529913/article/details/118676574
C++ STL vector容器(深入了解,一文学会)                       https://blog.csdn.net/qq_37529913/article/details/118676109
C++ STL list容器(深入了解,一文学会)                             https://blog.csdn.net/qq_37529913/article/details/118676917
C++ STL forward_list容器(深入了解,一文学会)               https://blog.csdn.net/qq_37529913/article/details/118687348
C++ STL array 容器(深入了解,一文学会)                        https://blog.csdn.net/qq_37529913/article/details/118688364
C++ STL pair 类模板(深入了解,一文学会)                       https://blog.csdn.net/qq_37529913/article/details/118714852
C++ STL map容器(深入了解,一文学会)                           https://blog.csdn.net/qq_37529913/article/details/118741670
C++ STL map emplace()和emplace_hint()(深入了解,一文学会)         https://blog.csdn.net/qq_37529913/article/details/118771777
C++ STL multimap容器(深入了解,一文学会)                    https://blog.csdn.net/qq_37529913/article/details/118773021
C++ STL Set容器(深入了解,一文学会)                             https://blog.csdn.net/qq_37529913/article/details/118918940
C++ STL multiset容器(深入了解,一文学会)                      https://blog.csdn.net/qq_37529913/article/details/119624779
C++ STL unordered_map容器(深入了解,一文学会)         https://blog.csdn.net/qq_37529913/article/details/119689199
C++ STL unordered_set容器(深入了解,一文学会)           https://blog.csdn.net/qq_37529913/article/details/119709019
C++ STL unordered_multiset容器(深入了解,一文学会)    https://blog.csdn.net/qq_37529913/article/details/119709079
C++ STL stack容器适配器(深入了解,一文学会)        https://blog.csdn.net/qq_37529913/article/details/119723782
C++ STL queue容器适配器(深入了解,一文学会)       https://blog.csdn.net/qq_37529913/article/details/119746246
C++ STL priority_queue容器适配器(深入了解,一文学会)                https://blog.csdn.net/qq_37529913/article/details/119770527
C++ STL reverse_iterator反向迭代器适配器(深入了解,一文学会)   https://blog.csdn.net/qq_37529913/article/details/119814820
C++ STL insert_iterator插入迭代器适配器(深入了解,一文学会)      https://blog.csdn.net/qq_37529913/article/details/119834378
C++ STL stream_iterator流迭代器(深入了解,一文学会)                  https://blog.csdn.net/qq_37529913/article/details/119834429
C++ STL streambuf_iterator流缓冲区迭代器(深入了解,一文学会)        https://blog.csdn.net/qq_37529913/article/details/119850048
C++ STL move_iterator移动迭代器(深入了解,一文学会)                      https://blog.csdn.net/qq_37529913/article/details/119859888
C++ STL advance()函数(深入了解,一文学会)        https://blog.csdn.net/qq_37529913/article/details/120008250
C++ STL distance()函数(深入了解,一文学会)        https://blog.csdn.net/qq_37529913/article/details/120008300
C++ STL iterator迭代器(深入了解,一文学会)         https://blog.csdn.net/qq_37529913/article/details/120008346
C++ STL const_iterator转换为iterator类型迭代器(深入了解,一文学会)        https://blog.csdn.net/qq_37529913/article/details/120008324
C++ STL begin()和end()函数(深入了解,一文学会)        https://blog.csdn.net/qq_37529913/article/details/120008459
C++ STL prev()和next()函数(深入了解,一文学会)         https://blog.csdn.net/qq_37529913/article/details/120008481

  • 2
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

双子座断点

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值