第16章——string类和标准模板库 - yongan1006的专栏 - 博客频道 - CSDN.NET
l string 类
string类是由头文件string支持的。头文件string.h和cstring支持对C-风格字符串时行操纵的C 库字符串函数。
string类的6个构造函数
构造函数 |
描述 |
string(const char*s) |
将string对象初始化为s指向的NBTS(空格结束的字符串) |
string(size_type n,char c) |
创建一个包含n个元素的string对象,每个元素创始化为c |
string(const string &str,string size_type n=npos) |
将string对象初始化为对象str中从位置pos开始到结尾的字符,或从位置pos开始的n个字符 |
string() |
创建一个默认的string对象,长度为0 |
string(const char *s,size_type n) |
将string对象初始化为s指向的NBTS中的前n个字符,即使超过了NBTS结尾 |
template <class Iter> string(Iter begin,Iter end) |
将string对象初始化为区间[begin,end)内的字符 |
string类的输入
对于C-风格的字符串
char info[100];
cin>>info;
cin.getline(info,100);//读一行,去除\n
cin.get(info,100);//读一行,保留\n
对于string对象
string stuff;
cin>>stuff;
getline(cin,stuff);
两个版本的getline都有一个可选参数,用于指定使用哪个字符来确定输入的边界
cin.getline(info,100,’:’);//读取到‘:’,去除’:’
getline(stuff,’:’);//读取到‘:’,去除’:’
两个版本的区别是string()版本的getline()将自动调整目标string对象的大小。
string对象可以被读取,可以被附加到另一个string对象中,还可以比较字符串,确定字符串的长度。(类方法size()和 length())。还可以在字符串中搜索给定的子字符串或字符。
方法原型 |
描述 |
size_type find(const string &str,size_type pos=0)const |
从字符串的pos位置开始,查找子字符串str.如果找到返回子字符串首次出现时的索引;否则,返回string:npos |
size_type find(const char *s,size_type pos=0)const |
从字符串的pos位置开始,查找子字符串s.如果找到返回子字符串首次出现时的索引;否则,返回string:npos |
size_type find(const char *s,size_type pos=0,size_type n) |
从字符串的pos位置开始,查找s的前n个字符组成的子字符串str.如果找到返回子字符串首次出现时的索引;否则,返回string:npos |
size_type find(char ch, size_type pos=0)const |
从字符串的pos位置开始,查找子字符ch.如果找到返回子字符串首次出现时的索引;否则,返回string:npos |
string库还提供了相关的方法:rfind()、find_first_of()、find_last_of()、find_first_not_of和find_last_not_of
rfind()方法是查找子字符串或字符最后一次出现的位置;find_first_of()方法在字符串查找参数中任何一个字符首次出现的位置。
eg:string snake1(“cobra”);
int where=snake1.find_first_of(“hark”);
将返回r在“cobra“中的位置(即索引3)
string方法capacity()返回当前分配给字符串的内在块的大小。
方法reserve()让你能够请求内在块的最小长度。
l auto_ptr类
auto_ptr是一个模板类,用于管理动态内在分配的用法。
要创建auto_ptr对象,必须包含头文件memory.
template <class X> class auto_ptr{
public:
explicit auto_ptr(X* p=0) throw();
…
};
eg:
auto_ptr<double> pd(new double);
auto_ptr<string> ps(new string);
使用auto_ptr指针,可以不用delete去释放内存。
使用auto_ptr时,不能用于new[],因为auto_ptr模板使用的是delete.
l STL
STL提供了一组表示容器、迭代器、函数对象和算法的模板。STL不是面向对象的编程,而是通用编程技术。
面向对象编程关注的是编程的数据方面,而通用编程技术关注的是算法。它们之间的共同点是抽象和创建可重用的代码。通用编程技术旨在编写独立于数据类型的代码,在C++中,完成通用程序的工具是模板。
模板使得算法独立于存储的数据类型,而迭代器使算法独立于使用的容器类型。
迭代器应具备的特征:
n 应能够对迭代器解除引用的操作,以便能够访问它引用的值
n 应能够将一个迭代器赋给另一个。
n 应能够将一个迭代器与另一个进行比较,看它们是否相等。
n 应能够使用迭代器遍历容器中的所有元素。这可以通过为迭代器p定义++p或p++来实现。
为了区分++操作符的前缀版本和后缀版本,C++将operator++()作为前缀版本,将operator++(int)作为后缀版本。
每个容器类定义了相应的迭代器类型。对于其中的某个类,迭代器可能是指针;而对于另一个类,则可能是对象。不管理实现方式如何,迭代器都提供所需的操作,如*和++。其次,每个容器都有一个超尾标记,当迭代器递增到超越容器的最后一个值后,这个值将被赋给迭代器。每个容器类都有begin()和end()方法,它们分别返回一个指向容器的第一个元素和超尾位置的迭代器。每个容器类都使用++操作,让迭代器从指向第一个元素逐步指向超尾位置,从而遍历容器中的每一个元素。
使用容器类时,无须知道其迭代器是如何实现的。
STL定义了5种迭代器,并根据所需要的迭代器对算法进行了描述。
迭代器 |
描述 |
输入迭代器 |
来自容器的信息被视为输入。输入迭代器可被程序用来读取容器中的信息。支持++操作符,输入迭代器是单向的,可以递增,不可以倒退 |
输出迭代器 |
输出,指用于将信息从程序传输给容器的迭代器。支持解除引用操作。它也是单向的。 |
正向迭代器 |
它可以按相同的顺序遍历一系列值,可以读取和修改数据。 |
双向迭代器 |
支持正向迭代器的所有特征,并支持两种递减(前缀和后缀)递减操作 |
随机访问迭代器 |
具有双向迭代器的所有特征,并支持随机访问的操作。 |
编写算法时,应尽可能使用要示最低的迭代器。各种迭代器的类型并不是确定的,而只是一种概念性描述。每个容器类都定义了一个类级 typedef名称——iterator.因此,vector<int>类的迭代器类型为vector<int>::interator。它的迭代器为随机访问迭代器。同样list<int>类的迭代器类型为list<int>::interator.它使用的是双向迭代器。
STL算法可以使用任何满足其要求的迭代器实现。迭代器是广义的指针,而指针满足所有的迭代器的要求。迭代器是STL算法的接口,而指针是迭代器,因此STL算法可以使用指针来对基于指针的非STL容器进行操作。
表示输出流的迭代器:
#include <iterator>
…
ostream_iterator<int,char> out_iter(cout,” ”)’
表示输入流的迭代器
istream_iterator<int,char> in_iter(cin)
除了ostream_iterator和istream_iterator之外,头文件iterator还提供了其他一些专用的预定义迭代器类型revese_iterator,back_insert_iterator,front_insert_iterator和insert_iterator
对reverse_iterator执行递增操作将导致它被递减,back_insert_iterator将元素插入到容器尾部,而front_insert_iterator将元素插入到容器的前端,insert_iterator将元素插入到insert_iterator构造函数的参数指定的位置前面。vector类不能使用front_insert_iterator实现的迭代器。
11个容器类型:dequeue、list、queue、priority_queue、stack、vector、map、multimap、set、multiset、bitset
容器概念指定了所有STL容器类都必须满足的一系列要求。基本容器不能保证其元素都按特定的顺序存储,也不能保证元素的顺序不变。
容器的基本特征
X表示容器类型,例如vector:T表示存储在容器中的对象类型;a和b表示类型X的值;u表示类型X的标识符。即X<T>a X上述的11个容器类型之一,T代表存储的对象的类型,a代表创建的容器名。例vector<string> words(4)
表达式 |
返回值类型 |
说明 |
复杂度 |
X::iterator |
指向T的迭代器类型 |
除输出迭代器之外的任何迭代器类型 |
编译时间 |
X::value_type |
T |
T的类型 |
编译时间 |
X u; |
|
创建名为u的空容器 |
固定 |
X(); |
|
创建匿名的空容器 |
固定 |
X u(a); |
|
复制构造函数 |
线性 |
X u=a; |
|
作用同X u(a); |
|
(&a)->-X |
void |
对容器中每个元素应用析构函数 |
线性 |
a.begin() |
迭代器 |
返回指向容器第一元素的迭代器 |
固定 |
a.end() |
迭代器 |
返回超尾值迭代器 |
固定 |
a.size() |
无符号类型 |
返回元素个数 |
固定 |
a.swap(b) |
void |
交换a和b的内容 |
固定 |
a==b |
可转换为bool |
如果a和b的长度相同,且a中每个元素都等于(==为真)b中相应的元素,则为真 |
线性 |
a!=b |
可转换为bool |
返回!(a==b) |
线性 |
可以通过添加要求来改进基本的容器概念。deque、list、queue、priority_queue、stack和vector都是序列。序列概念增加了迭代器至少是正向迭代器这样的要求。这保证了元素按特定的顺序排列,不会在两次迭代之间发生变化。数组和链表都是序列。
序列的要求
t 表示T类型的值,n表示整数,p、q、i和j表示迭代器
表达式 |
返回值类型 |
说明 |
X a(n,t) |
|
声明一个名为a的、由n个t值组成的序列 |
X(n,t) |
|
创建一个由n个t值组成的匿名序列 |
X a(i,j) |
|
创建一个名为a的序列,并将其初始化为区间[i,j)的内容 |
X(i,j) |
|
创建一个匿名序列,并将其初始化为区间[i,j)的内容 |
a.insert(p,t) |
迭代器 |
将t插入到p的前面 |
a.insert(p,n,t) |
void |
将n个t插入到p的前面 |
a.insert(p,i,j) |
void |
将区间[i,j)中的元素插入到p的前面 |
a.erase(p) |
迭代器 |
删除p指向的元素 |
a.erase(p,q) |
迭代器 |
删除区间[p,q]中的元素 |
a.clear() |
void |
等价于erase(begin(),end()) |
因为dequeue/list/queue/priority_queue/stack/vector模板类都是序列概念的模型,所以他们都支持上表的操作,除此之外,这6个模型中的一些还可使用其他的操作。在允许的情况下,它们的复杂度为固定时间。
表达式 |
返回值类型 |
含义 |
容器 |
a.front() |
T& |
*a.begin() |
vector/list/deque |
a.back() |
T& |
*--a.end() |
vector/list/deque |
a.push_front(t) |
void |
a.insert(a.begin(),t) |
list/deque |
a.push_back(t) |
void |
a.insert(a.end(),t) |
list/deque/vector |
a.pop_front(t) |
void |
a.erase(a.begin()) |
list/deque |
a.pop_back(t) |
void |
a.erase(--a.end()) |
vector/list/deque |
a[n] |
T& |
*(a.begin()+n) |
vector/deque |
a.at(t) |
T& |
*(a.begin()+n) |
vector/deque |
6种序列容器类型
(1) vector
vector是数组的一种类表示,它提供了自动内存管理功能。除了序列外,vector还是可反转容器概念的模型。这增加了两个类方法:rbegin()和rend()
vector模板类是最简单的序列类型
(2) deque
deque模板类(在deque头文件中声明)表示双端队列。它和vector的主要区别是在deque对象的开始位置插入和删除元素的时间是固定的。尽管二者都提供对元素的随机访问和在序列中部执行线性时间的插入和删除操作,但vector容器执行这些操作时速度要快些。
(3) list
list模板表示双向链表。list和vector之间的关键的区别在于,list在链表中任一位置进行插入和删除的时间都是固定的。vector模板提供了除结尾处外的线性时间的插入和删除,在结尾处,它提供了固定时间的插入和删除。因此,vector强调的是通过随机访问进行快速访问,而list强调的是元素的快速插入和删除。
与vector相似,list也是可反转容器。与vector不同的是,list不支持数组表示法和随机访问。
list的方法:
void merge(list<T,Alloc>&x) 将链表x与调用链表合并。
void remove(const T &val) 从链表中删除val的所有实例
void sort() 对链表进行排序
void splice(iterator pos,list<T,Alloc>x)将链表x的内容插入到pos的前面,x将为空。
void unique()将连续的相同元素压缩为单个元素。
(4) queue/priority_queue/stack它们都是适配器类
联合容器
联合容器是对容器概念的另一个改进。它的优点在于提供了对元素的快速访问。与序列类似,联合容器也允许插入新元素,不过不能指定元素的插入位置。
STL提供了4种联合容器:set/multiset/map/multimap
(1) set
它是一个联合集合,可反转,可排序,关键字是唯一的,所以它只能存储同一种类型的值。
eg:
set<string>A
set内的对象是经过排序的。
通用方法:
显示A和B的并集
set_union(A.begin(),A.end(),B.begin(),B.end(),ostream_iterator<string,char>out(cout,” “));
显示交集
set_intersection(A.begin(),A.end(),B.begin(),B.end(),ostream_iterator<string,char>out(cout,” “));
显示差集
set_difference(A.begin(),A.end(),B.begin(),B.end(),ostream_iterator<string,char>out(cout,” “));
multimap
与set相似,multimap也是可反转的、经过排序的联合容器。下面的声明将创建一个multimap对象,其中关键字的类型为int,所存储的值为string
multimap<int,string> codes;
为将信息结合在一起,实际的值类型将关键字类型和数据类型结合为一对。STL使用pair<class T,class U>模板类将这两种值存储到一个对象中
eg:
pair<const int,string> item(123,string);
codes.insert(item);
l 函数对象
很多STL算法都使用函数对象——也叫函数符。函数符是可以以函数方式与()结合使用的任何对象。这包括函数名、指向函数的指针和重载了()操作符的类对象
函数符概念
n 生成器是不用任何参数就可以调用的函数符
n 一元函数是用一个参数可以调用的函数符
n 二元函数是用二个参数可以调用的函数符
n 返回bool值的一元函数是断言
n 返回bool值的二元函数是二元断言
STL定义了多个基本函数符
操作符 |
相应的函数符 |
操作符 |
相应的函数符 |
+ |
plus |
> |
greater |
- |
minus |
< |
less |
* |
multiplies |
>= |
greater_equal |
/ |
divides |
<= |
less_equal |
% |
modulus |
&& |
logical_and |
- |
negate |
|| |
logical_or |
== |
equal_to |
! |
logical_not |
!= |
not_equal_to |
|
|
以上预定义函数符都是自适应的。自适应的意思就是能够自动找到函数相应的参数
l 算法
算法都是使用迭代器来标识要处理的数据区间和结果的放置位置。它们都使用模板来提供通用类型。
对于复制算法,统一的约定:返回一个迭代器,该迭代器指向复制的最后一个值的后面的位置。
有些函数有这样的版本,即根据将函数应用于容器元素得到的结果来执行操作,这些版本的名称通常以_if结尾。
strign类虽不是STL的组成部分,但设计它时考虑到了STL.它包含begin()/end()/rbegin()/rend()等成员。
有时,可以选择使用STL方法还是使用STL函数。通常方法是更好的选择。它可以使用模板类的内存管理工具。
eg:假设la是一个list<int>对象
la.remove(4);
链表中所有值为4的元素都将被删除,同时链表的长度将自动被调整
remvoe(al.begin(),lb.end(),4)
不能调整链表长度
使用STL时,应尽可能减少要编写的代码
l 其它库
C++提供了两个数组模板:vector和valarray.这些类是由不同的小组开发的,用于不同的目的。vector模板类是一个容器类和算法系统的一部分,它支持面向容器的操作,如排序、插入、重新排列、搜索、将数据转移到其他容器中等。而valarray类模板是面向数值计算的,不是STL的一部分。它为很多数学运算提供一个简单、直观的接口。