同样,list模板类也是一个容器。list是由双向链表来实现的,每个节点存储1个元素。list支持前后两种移动方向。list和vector类似,没有提供对元素的随机访问。list的优势在于任何位置执行插入和删除动作都非常迅速,因为改变的仅仅是链接而已,所以在list中移动元素要比在vector和deque中快得多。list模板类是定义于命名空间(name space)std中的,该类模板的声明形式为:
使用list需要包含头文件<list>。前面已经讲过,任意型别T只要具备可设置性和可复制性,即可作为list元素。模板的第2个参数可有可无,适用于指定内存模型。在模板中,第2个参数被指定了默认的内存模型为allocator。list的内部结构和vector不同,存在较明显的区别。
1)list不支持随机存取。
2)在list的任何位置执行元素的插入和移除都非常快,可以迅速实现。插入和删除动作不会影响指向其他元素的指针、引用、迭代器,不会造成失效。
3)list不提供下标操作符[]和at()函数。
4)list没有提供容量、空间重新分配等操作函数,每个元素都有自己的内存。
5)list也提供了特殊成员函数,专门用于移动元素。和同名的算法相比,使用这些函数速度更快。
list模板类实现了标准C++数据结构中链表的所有功能。一旦list定义了类对象,就可以完成链表操作。
下面介绍list类模板的使用方法。
1.list的定义和容量
(1)list的定义和构造函数
list模板类描述的对象控制是一个元素类型为T的可变长度序列。该序列以双向链表的方式存储。链表中的每个元素都包含一个类型为T的成员。list对象是通过存储于其内部的一个类A的分配器对象进行存储空间的分配和释放。该分配器对象必须拥有和模板类alloca-tor同样的外部接口。
头文件list中定义了四种构造函数。
以上四种构造函数中,第1个是默认的构造函数,表示要创建空list对象。根据以上构造函数,list对象定义时一般有以下几种方法:
以上几种定义list类型对象的方法中,第一种形式最简单。第一种示例可以创建1个空的list对象,其中可以容纳类型为A的元素,其名称为listname。例如,
而第二种示例可以创建初始大小为size的list对象;第三种示例可以创建初始大小为size,每个元素初始值为value的list对象;第四种示例用复制构造函数从现有的list中创建新的list对象;第五种方法创建1个list对象,并从其他list对象中复制由迭代器指定范围的多个元素。
(2)元素的赋值
list模板类提供了两个成员函数push_front()和push_back(),用来把新的元素插入到list对象中。push_front()函数用来在容器中的头部插入新元素,由于vector在头部插入新元素的效率非常低,因此仅在list中存在该函数。push_back()函数用于在容器的底部插入新元素。还有另外两个函数pop_front()和pop_back()分别用于获取容器的“头”元素和容器的“尾”元素。这4个函数的原型分别为:
与vector类模板不同的是,vector模板类只具有push_back()和pop_back();而list模板类具有4个相应的函数,充分说明list类型容器是双向链表。
(3)容器的容量
list对象作为容器,和其容量相关的成员函数包括resize()、size()和max_size()。其函数原型分别为:
size()和max_size()函数的返回类型均为size_type型,即unsignedint类型。size()函数用于返回list对象(容器)中元素的个数;max_size()函数用于返回list对象的最大允许容量(一般是一个非常大整数)。resize()函数用于重新调整list对象的大小。
(4)迭代器相关
list模板类所包含的和迭代器相关的函数主要有begin()、front()、rbegin()、end()、back()和rend()。这几个函数的原型分别为:
下面以例3-12说明以上4个知识点的使用方法。通过实例学习,希望读者对list类型容器的最基本知识有所了解,理解list的定义、容量、赋值方法以及该容器中和迭代器相关的成员函数。
例3-12
注意:请关注第1行和3个不同的print()函数的使用方法。
请关注例3-12源代码中的中文注释。例3-12还使用了模板函数和仿函数以及for_each()算法函数,还对list容器的大小进行了重新设置。为防止程序编译时显示过多的警告信息,在程序起始位置添加了“#pragma warning(disable:4786)”语句。读者应对照源代码和程序输出结果,认真理解其用法。
提示
①例3-12中,由于end()函数所指向的是容器中最后一个元素的后面的位置,因此程序中输出end()返回的数值时,并不是最后一个元素的数值。rend()函数同理。由此想到,在使用迭代器进行for循环时,一般循环终止条件是(iterator!=end()),即循环至end()返回值指向的位置时(恰好是容器中最后一个元素的后面),停止循环。
②Print_D(double&Ele)函数中,使用了cout的格式化输出。
例3-12的执行效果如图3-13所示。
2.list容器的基本成员函数
list容器最基本的应用包括判断list内容是否为空、元素的存取和访问、元素重置、交换两个list型容器的内容、元素的插入和删除等。
(1)判断容器是否为空
若list型容器中为空,则成员函数empty()返回true。empty()函数的原型为:
图3-13 例3-12的执行效果
例3-13
由例3-13可知,使用empty()函数可以比较方便地判断容器是否为空。
(2)元素的存取和访问
list型容器不提供成员函数at()和操作符[],这对容器中的元素访问无疑是不便的。值得庆幸的是,还可以使用迭代器进行元素的访问。例如,
例3-14
例3-14的执行效果如图3-14所示。
图3-14 例3-14的执行效果
(3)元素重置
list型容器提供了可重置元素值的成员函数assign()。使用assign()函数可以修改容器中任意元素的数值,甚至可以修改多个连续元素的数值。assign()函数的原型为:
下面举例说明其使用方法。
例3-15
例3-15涉及了assign()函数的两种用法。在需要初始化list型容器或者复制list型容器中的元素时,使用assign()函数是非常方便的。例3-15的执行效果如图3-15所示。
图3-15 例3-15的执行效果
提示
学习assign()函数的使用方法时,请关注函数模板print()。关注一下即可,后面会专门讲解。
(4)交换两个list型容器的内容
list模板类提供了成员swap()函数,可用以实现两个list型容器(对象)的内容交换。通过swap()函数完成两个list型容器中内容交换的同时,两个参与交换的容器大小也发生变化。STL的算法库也提供了swap()函数。作为通用算法,swap()函数同样可以实现两个list型容器中的内容交换。如例3-16所示,在例3-15的基础上进行适当的修改,添加了用于容器内容交换的代码。
例3-16
例3-16的执行效果如图3-16所示。
(5)元素的插入和删除
list模板类和其他型容器一样,提供了丰富而灵活的插入和删除元素操作函数。list型容器甚至可以在序列的开头和队尾灵活地插入和删除元素。涉及的成员函数主要包括push_back()、push_front()、pop_back()、pop_front()、insert()、erase()和clear()。push_front()和push_back()均可以轻松地将元素插入至序列中,pop_back()和pop_front()亦可以轻松地从首端或尾端将元素从序列中移除。这4个函数在前面章节已有涉及,此处不再赘述。本小节重点介绍insert()、erase()和clear()函数。
图3-16 例3-16的执行效果
成员函数insert()的原型为如下3种型式:
第一种形式的作用是把某个元素插入到指定位置;第二种形式的作用是把某个具体值的多个备份插入到list中迭代器所指的起始位置;第三种形式的作用是把指定范围的多个元素插入到list中迭代器所指的范围中。该成员函数的使用方法和vector型容器的同名成员函数类似。
成员函数erase()的原型为如下两种型式:
同样,第一种形式的作用是删除迭代器指向的元素;第二种形式的作用是删除迭代器所定义的范围。该函数的使用方法和vector型容器的同名成员函数近似。
成员函数clear()相当于使用erase()函数删除序列中的所有元素,即erase(begin(),end())。修改例3-9,使之适用于list型容器。
例3-17
例3-17的执行效果如图3-17所示。
图3-17 例3-17的执行效果
总结(www.xing528.com)
例3-17是在例3-9的基础上修改而来的(仅将头文件<vector>修改为<list>,将容器的定义语句vector<int>myvt修改为list<int>mylt)。请读者关注上述代码中的黑体字,并重点体会pop_font()、pop_back()、erase()和clear()的使用方法。
3.运算符函数
同样,list型容器也提供了大量的函数模板,即提供了大量运算符函数。常见的运算符函数包括operator ==、operator<、operator! =、operator<=、operator>、operator>=等。
(1)operator ==
operator[]主要用于读取list中的元素;而operator==则是判断语句,用于判断两个list型容器是否相等。如果相同,返回true;否则,返回false。其原型为:
上述代码表明,参与比较的两个list对象的格式应该完全一样,不能使用两个类型不同的容器进行比较。
(2)operator<
operator<用于判断两个list型容器是否“前者小于后者”。如果“前者小于后者”,返回true;否则,返回false。其原型为:
(3)operator!=
operator!=用于判断两个list型容器是否“不相等”。如果两者不同,返回true;否则,返回false。其原型为:
(4)operator<=
operator<=用于判断两个list型容器是否“前者小于或等于后者”。如果“前者小于或等于后者”,返回true;否则,返回false。其原型为:
(5)operator>
operator>用于判断两个list型容器是否“前者大于后者”,如果“前者大于后者”,返回true;否则,返回false。其原型为:
(6)operator>=
operator>=用于判断两个list型容器是否“前者大于或等于后者”。如果“前者大于后者”,运算符返回true;否则,返回false。其原型为:
下面使用例3-18说明以上6个运算符函数的使用方法。
例3-18
例3-18的执行效果如图3-18所示。
图3-18 例3-18的执行效果
4.其他重要成员函数
list型容器具有一些特殊函数,如merge()、remove()、remove_if()、sort()、splice()和unique()。
(1)merge()函数和sort()函数
merge()函数可以将两个list型对象合并成一个list对象。该函数的功能在于把原型中的list型容器对象作为函数参数,插入到目标list(即函数的调用者)中。合并之后的容器中元素是按从小到大升序排列的。其原型为:
list型容器还提供了具有排序功能的成员函数sort(),用于对list型容器中的元素进行排序。sort()函数默认的排序方式是从小到大。其原型为:
以上两个函数的具体使用方法参见例3-19。
例3-19
在上述代码中,两个list型容器合并之后,新容器中的元素是自动排序的。
上述语句实现了对容器中的元素降序排列。函数的参数“greater<int>()”是STL中的预定义仿函数。List型容器的成员函数sort()默认的排序方式是升序。
例3-19的执行效果如图3-19所示。
图3-19 例3-19的执行效果
(2)remove()函数和remove_if()函数
删除list中的对象可以使用pop_back()、pop_front()、erase()、clear()等常见函数。List型容器还提供了remove()函数,其原型为:
该函数可以删除list型容器中某个具体的元素。使用remove()函数不需要指定具体位置,只要告诉需要删除的元素的值,就可以直接删除所有相应的元素。这是和其他删除函数不同的地方。值得注意的是,使用remove()函数并不改变容器中元素的顺序。同时,由于在使用remove()函数时,会删除掉所有等于参数值(指定数值_Val)的元素,程序员要谨慎使用。
remove_if()函数是有条件地删除所有等于参数值的list型容器中的元素。其原型为:
remove_if()函数仅删除满足条件“Pred pr”的相应元素。“Pred pr”参数是一元谓词。在Visual C++ 6.0中,Pred的定义如下:
只有当Pred pr为true时,remove_if()函数才能正确执行删除操作。在visual C++ 6.0编译环境下,该函数的执行效果是删除和指定参数pr不相等的所有元素。请读者参考例3-20,仔细体会这两个删除函数的用法。
例3-20
例3-20的执行效果如图3-20所示。
图3-20 例3-20的执行效果
提示
学习remove()函数和remove_if()函数的同时,请读者详读这两个函数的功能。最重要的是关注成员函数remove_if()的使用。这不同于算法remove_if()的使用,算法remove_if()在使用时,要求更宽泛一些。
(3)splice()函数
前面讲述了容器合并成员函数merge()。该函数使用起来并不是非常灵活,反而具有一定的局限性。list型容器提供了另一个函数splice()。其原型为:
第一种形式既可以把list型对象x插入在迭代器指针it指定的位置后面;第二种形式可以将x中的某个元素(first指向的元素)插入it的后面;第三种形式可以将x中某个范围(first,last)内的元素插入在it后面。值得注意的是,一旦合并完成,参数x中会减少相应数目的元素。因为这些元素已经转移走了。尤其是第一种形式,一旦合并函数splice()执行成功之后,参数x中将不包含任何元素——空容器。在例3-19的基础进行修改,使其可以体现splice()函数的用法。
例3-21
例3-21的执行效果如图3-21所示。
图3-21 例3-21的执行效果
提示
学习list型容器的成员函数splice()时,要关注合并以后,参数x表示的容器中的元素数目。同时应该注意对于不同的编译系统,对于STL的容器模板可能略有差别。
(4)unique()函数
对于list型容器中存储的元素,可能存在连续的多个元素数值相等的情况,使用unique()函数会移除重复元素,仅留下1个。其原型有如下两种形式:
splice()函数假定容器中元素是已排序的,因此所有相同的元素都是相邻的。不相邻的重复元素不能被移除。对于第二种函数形式,只有当元素满足条件表达式_Pred时,该元素才被删除。由以上可知,unique()函数并不能保证序列中元素值的唯一性,而仅仅是将相邻的重复元素保留一个。该函数的第二种原型:template<class BinaryPredicate>void unique(BinaryPredicate _Pred),其中参数BinaryPredicate _Pred在Visual C++ 6.0中其具体形式如下:
所以,第二种形式所实现的功能是仅保留和第一个元素相等的元素,参数仅提供“函数”,即仅提供谓词。此时参数的实例化问题较突出。和前面的remove_if()函数有相似之处。
例3-22
例3-22的执行效果如图3-22所示。
图3-22 例3-22的执行效果
提示
①使用list型容器的成员函数unique()时,尽量先使用sort()函数对容器中的元素进行排序;
②使用unique()函数的第二种形式时,参数仅是“谓词”,而不提供具体的数值!
(5)reverse()函数
reverse()函数可将容器中的所有元素与原来相反的顺序排列。其原型为:
在使用reverse()函数时,不需要任何参数,仅调用即可。容器中的所有元素会按与原来相反的顺序排列。
总结
这里再次对list模板类做简要说明。list型容器是非常重要的一种模板类。遗憾的是,它没有提供操作符[]和成员函数at()。list型容器提供了很多可实现序列操作功能的函数。在学习以上内容的同时,读者应注意反复多读几遍,以充分掌握这些知识点。
免责声明:以上内容源自网络,版权归原作者所有,如有侵犯您的原创版权请告知,我们将尽快删除相关内容。