C++标准模板库Stand Template Library(STL)简介与STL string类
参考《21天学通C++》第15和16章节,在对宏和模板学习之后,开启对C++实现的标准模板类STL进行简介,同时介绍简单的string类。虽然前面对于vector、deque、list等进行过学习和总结,但并没有一个宏观上的把握,现在通过上一篇和这一篇博文,将对C++模板以及基于C++模板的STL关联起来,形成一个总体的把握,对于掌握C++中模板(template)这一强有力的工具会十分有帮助。本文的主要内容有:
(1) STL容器;
(2) STL迭代器;
(3) STL算法;
(4) STL字符串类。
简单的来说,标准模板库是一组模板类和函数,向程序员提供了用于存储信息的容器、用于访问容器存储信息的迭代器和用于操作容器内容的算法。最后结合非常常用的STL string类进行介绍。
一、STL标准模板库
************************************************************************************************************************************1. STL容器
容器是用于存储数据的STL类,STL提供了两种类型的容器:顺序容器和关联容器。另外,还提供了容器适配器(Container Adapter)的类,它们是顺序容器和关联容器的变种,用于满足特殊需求。
(1) 顺序容器
按照顺序存储数据,比如数组和列表。顺序容器插入速度快但查找操作相对较慢。主要包括以下几种标准模板类实现:std::vector,操作与动态数组一样,在最后插入数据;std::deque,与vector类似,可在开头插入和删除元素;std::list,操作与双向链表一样,可视为链条,对象被连接在一起,可在任意位置插入和删除对象;std::forward_list,类似std::list,是单向链表,只能沿着一个方向遍历。
(PS:如果上面介绍的数据结构不太清楚的话,可以参考我的系列博文:数据结构(二):线性表的使用原则以及链表的应用-稀疏矩阵的三元组表示http://blog.csdn.net/lg1259156776/article/details/47073957;数据结构(二):链表、链队列http://blog.csdn.net/lg1259156776/article/details/47018813;数据结构(二):线性表包括顺序存储结构(顺序表、顺序队列和顺序栈)和链式存储结构(链表、链队列和链栈) http://blog.csdn.net/lg1259156776/article/details/46993591)
正如上面博文中介绍的,vector和deque是顺序存储结构,而list是链式存储结构,vector和deque都是连续的内存组织,而list则可使用不连续的内存块组织,所以list不需要像vector和deque那样给内部数组重新分配内存。所以在使用中,涉及到频繁插入和删除操作的,一般使用链式存储结构的list,否则一般使用vector足矣。
(2) 关联容器
关联容器按指定的顺序存储数据,就像词典一样,这将降低插入数据的速度,但是在查询方面有很大的优势。
STL提供的关联容器包括:
std::set,存储各不相同的值,在插入时进行排序;容器的复杂度为对数。
std::unordered_set,存储各不相同的值,在插入时进行排序;容器的复杂度为常数。
std::map,存储键值对,并根据唯一的键排序;容器的复杂度为对数。
std::unordered_map,存储键值对,并根据唯一的键排序;容器的复杂度为对数。
std::multiset,与set类似,但允许存储多个值相同的项,即值不需要是唯一的。
std::unordered_multiset,与unordered_set类似,但允许存储多个值相同的项。
std::multimap,与map类似,但不要求键是唯一的。
std::unordered_multimap,与unordered_map类似,但不要求键是唯一的。
(3) 选择合适的容器
显然,可能有多种STL容器能够满足应用程序的需求,但哪一个是较好的选择呢?
下面对STL容器的特点进行梳理:
std::vector(顺序容器)的优点是在末尾插入数据时快(时间固定),可以像访问数组一样进行访问,缺点是调整大小时将影响性能,搜索时间与容器包含的元素个数成正比,只能在末尾插入数据;
std::deque(顺序容器)的优点是具备vector所有优点,还可以在容器开头插入数据,插入时间固定。缺点是:vector所有缺点,与vector不同的是根据规范,deque不需要支持reserve函数,该函数让程序元能够给vector预留内存空间,以免频繁地调整大小,从而提高性能。
std::list(顺序容器)的优点是在list开头、中间或末尾插入数据,所需时间都是固定的,将元素从list中删除所需的时间是固定的,而不管元素的位置如何,插入或删除元素后,指向list中其他元素的迭代器仍然有效,缺点是不能像数组那样能够根据索引随机访问元素,搜索速度比vector慢,因为元素没有存储在连续的内存单元中,搜索时间与容器的个数成正比。
std::forward_list(顺序容器)优点是单向链表类,只能沿一个方向遍历,缺点是只能使用push_front在链表表头插入元素。
std::set(关联容器),搜索时间不是与容器中的元素个数而是与元素个数的对数成正比,因此搜索速度通常比顺序容器要快,缺点是元素的插入速度比顺序容器慢,因为插入时要进行排序。
std:unordered_set(关联容器),搜索、插入和删除速度几乎不受容器元素个数的影响。缺点是由于元素未被严格排序,因此不能依赖于元素在容器中的相对位置。
std::multiset(关联容器)优点是需要存储非唯一值时,应使用;缺点是插入速度可能比顺序容器要慢,因为在插入时对元素键值对进行排序。
std::unordered_multiset(关联容器)优点是需要存储非唯一值时,搜索、插入和删除速度几乎不受容器元素个数的影响。缺点是由于元素未被严格排序,因此不能依赖于元素在容器中的相对位置。
std::map(关联容器)优点是可用于存储键值对,搜索时间与元素个数的对数成正比,因此搜索速度通常比顺序容器要快的多。缺点是插入时进行排序,因此插入速度比顺序容器要慢。
std::unordered_map(关联容器)优点是搜索、插入和删除元素的时间固定,不受容器长度的影响。缺点是元素未被严格排序,不适合用于顺序很重要的情形。
std::multimap(关联容器)优点是在需要存储键值对且要求键值不唯一时,可用。缺点是插入时需要排序,因此插入速度比顺序容器要慢。
std::unordered_multimap(关联容器)优点是在需要存储键值对且要求键值不唯一时可用。缺点是由于元素未被严格排序,因此不能依赖于元素在容器中的相对位置。
(4) 容器适配器
是顺序容器和关联容器的变种,其功能有限,用于满足特定的需求,主要的容器适配器有:
std::stack,以后进先出的方式存储元素,能够在栈顶压入和弹出元素;
std::queue,以先进先出的方式存储元素,能够删除最先插入的元素;
std::priority_queue,以特定的顺序存储元素,因为优先级最高的元素总是位于队列开头。
************************************************************************************************************************************
2. STL迭代器
最简单的迭代器是指针,给定一个指向数组中第一个元素的指针,可递增该指针使其指向下一个元素,还可以直接对当前位置的元素进行操作。
STL中的迭代器是模板类,从某种程度上说,它是泛型指针,能够对STL各种容器进行访问。注意,操作也可以是以模板函数的方式提供的STL算法,迭代器是一个桥梁,用来沟通STL算法和STL容器。
STL提供的迭代器分为两大类,一是输入迭代器,通过对输入迭代器解除引用,它将引用对象,而对象可能位于集合中,最严格的输入迭代器确保只是以只读的方式访问对象;二是输出迭代器,输出迭代器让程序员对集合执行写入操作,最严格的输出迭代器确保只能执行写入操作。
以上两种迭代器可进一步分为三种:一是前向迭代器,允许输入和输出,还可以是const只读的,前向迭代器通常用于单向链表;二是双向迭代器,可对其进行递减操作,从而向后移动,常用于双向链表;三是随机访问迭代器,常用于访问数组。
************************************************************************************************************************************
3. STL算法
对应STL中的容器,STL实现了一系列的模板函数。查找、排序和翻转都是标准的编程需求,不应让程序员重复实现这样的功能,因此,以STL以STL算法的方式提供这些函数,通过结合使用这些函数和迭代器,程序员可对容器执行一些最常见的操作,比如:
std::find,在集合中查找值;
std::find_if,根据用户提供的谓词在集合中查找值;
std::reverse,翻转集合中的元素;
std::remove_if,根据用户定义的谓词在集合中删除元素;
std::transform,使用用户定义的变换函数对容器中的元素进行变化。
************************************************************************************************************************************
4. STL string类
STL提供了一个专门操作字符串而设计的模板类,std::basic_string<T>该模板类两个常用的具体化如下:
std::string,基于char型的std::basic_string具体化,用于操作简单的字符串;
std::wstring,基于wchar_t的std::basic_string具体化,用于操作宽字符串。
************************************************************************************************************************************
二、STL string类
下面主要针对string具体化进行介绍:
std::string是一个类似于动态字符串的数据结构,通常的字符串的存储结构是基于字符数组的,但是其长度是固定(static),使用起来十分不方便。使用string能够有以下的优势:
减少在创建和操作字符串方面的工作量;
在内部管理内存分配细节,从而提高了应用程序的稳定性;
提供了复制构造函数和赋值运算符,可确成员字符串得以正确复制;
提供了帮助执行复制、截短、查找和删除的使用函数。
提供了用于比较的运算符;
1. 使用STL string类
(1) 基本操作
最常用的字符串函数包括:复制、连接、查找字符和字符串、截短、使用标准模板库提供的算法进行字符转反转和大小写转换。必须包含<string>
(2) 实例化和复制STL string
使用常量字符串初始化,赋值操作即可。
std::string str("test")
(3) 访问string的字符内容
string的访问可以使用迭代器,也可以采用数组的语法下标运算符[]来提供偏移量。要获得string对象的C风格表示,可使用成员函数c_str(),转变为char* 的表示。
string::const_iterator iCharacterLocator;
(4) 拼接字符串
可使用+=来操作,也可以使用成员函数append,append函数有多个重载版本,能够接受一个string,也可以接受C风格的char* 字符串。
(5) 查找字符串或查找字符
成员函数find(),该函数有多个重载版本,可在给定的string对象中查找字符或字符串。还提供了一些与find类似的函数如:find_first_of(),find_first_not_off,find_last_off(),find_last_not_off。
(6) 截短字符
可使用erase函数,在给定偏移量和字符数时删除指定数目的字符;在给定指向字符的迭代器时删除该字符;在给定由两个迭代器指定的范围时删除该范围内的字符。string类还提供了clear,用来清除所有的内容,并重置string对象。
(7) 字符串反转
有时需要翻转字符串的内容,假设要判定用户输入的字符串是否回文,方法之一就是将其翻转,再与原来的字符串比较。翻转STL string很容易,只需要使用泛型算法:std::reverse即可。通过迭代器输入翻转的范围,可以在一部分的字符串上进行翻转。
(8) 字符串的大小写转换
可使用算法transform,它对集合中的每一个元素都执行一个用户指定的转换函数,这里集合是string对象本身。
如果应用程序需要支持非拉丁文字符,如中文和日文,应使用std::wstring,其操作方式与string一样。
小结:
STL string类是模板库提供的一个容器,可满足程序员众多字符串操作,优点是原来由程序员负责维护的内存等操作,将有STL框架提供的一个容器类完成。
***********************************************************************************************************************************
2015-7-31