16. 1 sting类
1.string类的构造函数

2.string类输入
- 对于C-风格字符串,有3种方式:
char info[100];
cin >> info; //read a word
cin.getline(info, 100); //read a line, discard '\n'
cin.get(info, 100); //read a line,leave '\n' in queue
- 对于string对象,有2种方式:
string stuff;
cin >> stuff; //read a word
getline(cin,stuff); //read a line, discard '\n'
两个版本的getline()都有一个可选参数,用于指定使用哪个字符来确定输入的边界,注意:将‘:'指定为分界符号后,换行符将被视为常规字符。
cin.getline(info, 100, ':'); //read up to ':',diacard ':'
getline(stuff, ':'); //read up to ':',diacard ':'
在功能上,它们之间的主要区别在于,string版本的getline()将自动调整目标string对象的大小,使之刚好能够存储输入的字符。功能的底层实现:C++分配一个比实际字符串大的内存块,为字符串提供了增大空间。然而,如果字符串不断增大,超过了内存块的大小,程序将分配一个大小为原来两倍的内存块,以提供足够的增大空间,避免不断地分配新的内存。
3.string类读取字符串结束条件
- 到达文件尾;
- 遇到分界符号(默认为‘\n’);
- 读取的字符数达到最大允许值。
4.重载find()方法
可以以多种不同的方式在字符串中搜索给定的字符串或字符。其中,npos变量是string类的静态成员,它的值是string对象能储存的最大字符数,由于索引从0开始,所以它比最大的索引值大1,因此可以使用它来表示没有查找到字符或字符串。
string库还提供了相关的方法:rfind()、find_first_of()、find_last_of()、find_first_not_of()和find_last_not_of(),他们的重载函数特征标都与find()方法相同。rfind()方法查找子字符串或字符最后一次出现的位置;find_first_of()方法在字符串中查找参数中任何一个字符首先出现的位置;find_fisrt_not_of()方法在字符串中查找第一个不包含在参数中的字符。
5.string函数提供的功能
- 删除字符串的部分或全部内容;
- 用一个字符串的部分或全部内容替换另一个字符串的部分或全部内容;
- 将数据插入到字符串或删除字符串中的数据;
- 将一个字符串的部分或全部内容与另一个字符串的部分或全部内容进行比较;
- 从字符串中提取子字符串;
- 将一个字符串中的内容复制到另一个字符串中;
- 交换两个字符串的内容。
注:此部分内容需要参考附录将函数整理归纳
16. 2 智能指针模板类
6.三个智能指针
三个智能指针模板(auto_ptr、unique_ptr和shared_ptr)都定义了类似指针的对象,可以将new获得(直接或间接)的地址赋给这种对象。当智能指针过期时,其析构函数将使用delete来释放内存。因此,如果将new返回的地址赋给这些对象,将无需记住稍后释这些内存;在智能指针过期时,这些内存将自动被释放。下图说明了auto_ptr和常规指针在行为方面的差别:shared_ptr、unique_ptr的行为和auto_ptr相同。

7.智能指针的使用
原函数remodel()如下:
void remodel(std::string &str)
{
std::string *ps = new std::string(str);
...
if(weird_thong())
throw exception();
str = *ps;
delete ps;
return;
}
使用智能指针应按照下面3个步骤进行:
- 包含头文件memory;
- 将指向string的指针替换为指向string的智能指针对象;
- 删除delete语句
使用智能指针的remodel()函数代码如下:
#include <memory>
void remodel(std::string &str)
{
std::auto_ptr<std::string> ps(new std::string(str));
...
if(weird_thong())
throw exception();
str = *ps;
//delete ps; NO LONGER NEEDED
return;
}
8.智能指针的使用注意事项
- 使用new分配内存时,才能使用auto_ptr和shared_ptr,使用new[ ]分配内存时,不能使用它们;
- 不使用new分配内存时,不能使用auto_ptr或shared_ptr;
- 不使用new或new[ ]分配内存时,不能使用unique_ptr。
9.使用哪种智能指针的情况
- shared_ptr:程序要使用多个指向同一个对象的指针;
- unique_ptr:程序不需要多个指向同一个对象的指针;
- auto_ptr:在满足unique_ptr要求的条件时,也可使用auto_ptr,但unique_ptr是更好的选择。
16. 3 标准模板库
10.STL提供的模板
- 容器:容器是一个与数组类似的单元,可以储存若干个值。STL容器是同质,即储存的值的类型相同;
- 算法:算法是完成特定任务(如对数组进行排序或在链表中查找特定值)的处方;
- 迭代器:迭代器能够用来遍历容器的对象,与能够遍历数组的指针类似,是广义指针;后文另一种说法:迭代器是一个广义指针,事实上,它可以是指针,也可以是一个可对其执行类似指针的操作——如解除引用(如operator*())和递增(如operator++())——的对象;
- 函数对象是类似与函数的对象,可以是类对象或函数指针(包括函数名,因为函数名被用作指针)
STL使得能够构造各种容器(包括数组、队列和链表)和执行各种操作(包括搜索、排序和随机排序)。
11. STL不是面向对象的编程,而是一种不同的编程模式——泛型编程。
12. 模板类vector
要使类成为通用的,应将它设计为模板类,STL正是这样做的——在头文件vector中定义了vector模板。
要创建vector模板对象,可使用通常的表示法来指出要使用的类型。另外,vector模板使用动态内存分配,因此可以用初始化参数来指出需要多少矢量:
#include vector
vector<int> rating(5);
13.可对矢量执行的操作
除分配储存空间外,vector模板或者说所有的STL容器都提供了一些基本方法,其中包括:
- size():返回容器中元素数目;
- swap():交换两个容器的内容;
- begin():返回一个指向容器中第一个元素的迭代器;
- end():返回一个表示超过容器尾的迭代器。
vector模板类包含一些只有某些容器才有的方法。 - push_back():将元素添加到矢量末尾;
- erase():删除矢量中给定区间的元素;
- insert():插入元素
注:此部分内容需要参考附录将函数整理归纳
14.对矢量可执行的其他操作
程序员通常要对数组执行很多操作,如搜索、排序、随机排序等。矢量模板类包含了执行这些常见的方法吗?没有!STL从更广泛的角度定义了非成员函数来执行这些操作,即不是为了每个容器而定义一个成员函数,而是定义了一个适用于所有容器类的非成员函数。这种设计理念省去了大量重复的工作。
下面来看3个具有代表性的STL函数:for_each()、random_shuffle()和sort()。
- for_each()函数可用于很多容器类,它接受3个参数。前面两个是定义容器中区间的迭代器,最后一个是指向函数的指针(更普遍地说,最后一个参数是函数对象)。for_each()函数将被指向的函数应用于容器区间的各个元素。被指向的函数不能修改容器元素的值。可以用for_each()函数来代替for循环。例如:
#include<vector>
strcut Review{
std::string title;
int rating;
};
vector<Review> book;
vector<Review>::iterator pr;
for(pr = book.begin(); pr!= book.end(); pr++)
showReview(*pr);
替换为:
for_each(book.begin(), book.end(), showReview);
- random_shuffle()函数接收两个指定区间的迭代器参数,并随机排列该区间中的元素。
random_shuffle(book.begin(), book.end());
- sort()函数有两个版本。
第一个版本接收两个定义区间的迭代器参数,并使用为储存在容器中类型元素定义的<运算符,对区间中的元素进行操作。例如下面的语句按升序对coolstuff的内容进行排序,排序时使用内置的<运算符对值进行比较:
vector<int> coolstuff
...
sort(coolstuff.begin(), coolstuff.end());
第二个版本,容器元素是用户定义的对象,则要使用sort(),必须定义能够处理该类型对象的operator()函数。例如,如果为Review提供了成员或非成员函数operator<(),则可以对包含Review对象的矢量进行排序。由于Review是一个结构,因此其成员是共有的,这样的非成员函数将为:
bool operator<(const Review & r1, const Review & r2)
{
if (r1.title < r2.title)
return true;
else if(r1.title ==r2.title && r1.rating < r2.rating)
return true;
else
return false;
}
有了这样的函数后,就可以对包含Review对象(如book)的矢量进行排序:
sort(book.begin(), book.end());
另外,如果想按降序排列或是按rating排列,则可以使用另一种格式的sort()。它接收3个参数,前两个参数也是指定区间的迭代器,最后一个参数是指向要使用的函数指针(函数对象),而不是用于比较的operator()。
bool WorseThan(const Review & r1, const Review & r2)
{
if( r1.rating > r2.rating)
return true;
else
return false;
}
有了这个函数就可以使用下面的语句将包含Review对象的book矢量按rating降序排列:
sort(book.begin(), book.end(),WorseThan);
16. 4 泛型编程
15. STL是一种泛型编程。面向对象编程关注的是编程的数据方面,而泛型编程关注的是算法。
16. 泛型编程旨在使用同一个find函数来处理数组、链表或任何其他容器类型。即函数不仅独立于容器中储存的数据类型,而且独立于容器本身的数据结构。模板提供了储存在容器中的数据类型的通用表示,因此还需要遍历容器中的值的通用表示,迭代器正是这样的通用表示。
17. 迭代器应具备的特征
- 应能够对迭代器执行解除引用的操作,以便能够访问它引用的值。即如果p是一个迭代器,则应对*p进行定义。
- 应能够将一个迭代器赋给另一个。即如果p和q都是迭代器,则应对表达式p=q进行定义。
- 应能够将一个迭代器与另一个迭代器进行比较,看它们是否相等。即如果p和 q都是迭代器,则应对p==q和p!=q进行定义。
- 应能够使用迭代器遍历容器中的所有元素,这可以通过为迭代器p定义++p和p++来实现。
18. 使用容器类时,无需知道其迭代器时如何实现的,也无需知道超尾时如何实现的,而只需知道它有迭代器,其begin()返回一个指向第一个元素的迭代器,end()返回一个指向超尾位置的迭代器即可。
例如,假设要打印vector< double >对象中的值,则可以这样做:
vector<double>::iterator pr;
for(pr = scores.begin(); pr != scores.end(); pr++)
cout << *pr << endl;
使用C++11新增的自动类型推断可进一步简化:对于矢量或列表,都可以使用如下代码:
for(auto pr = scores.begin(); pr != scores.end(); pr++)
cout << *pr << endl;
实际上,作为一种编程风格,最好避免直接使用迭代器,而应尽可能使用STL函数(如for_each())来处理细节。也可以使用C++新增的基于范围的for循环:
for(auto x : scores) cout << x << endl;
19. STL方法总结
首先时处理容器的算法,应尽可能用通用的术语来表达算法,使之独立于数据类型和容器类型。为使通用算法能够适用于具体情况,应定义能够满足算法需求的迭代器,并把要求加到容器设计上,即基于算法的要求,设计基本的迭代器的特征和容器特征。
20. 迭代器类型
- 输入迭代器
术语“输入”是从程序的角度说的,即从来自容器的信息被视为输入,就像来自键盘的信息对程序来说是输入一样。因此,输入迭代器可被程序用来读取容器中的信息。
- 输出迭代器:
STL使用术语“输出”来指用于将信息从程序传输给容器的迭代器,因此程序的输出就是容器的输入。
- 正向迭代器
与输入迭代器和输出迭代器相似,正向迭代器只是用++运算符来遍历容器,所以它每次沿容器向前移动一个元素;然后,与输入和输出迭代器不同的是,它总是按相同的顺序遍历一系列值。
- 双向迭代器
双向迭代器具有正向迭代器的所有特性,同时支持两种(前缀和后缀)递减运算符。
- 随机访问迭代器
有学算法(如标准排序和二分检索)要求能够直接跳到容器中的任何一个元素,这叫做随机访问,需要随机访问迭代器。
21. 迭代器性能

为何需要这么多的迭代器,目的是为了在编写算法尽可能使用要求低的迭代器,并让它适用于容器的最大区间。
注意,各种迭代器的类型并不是确定的,而只是一种概念性描述。每个容器类都定义了一个等级typedef名称——iterator,因此vector< int >类的迭代器为vector< int>::iterator。然后,该类的文档指出,矢量迭代器是随机访问迭代器,它允许使用基于任何迭代器类型的算法,因为随机迭代器具有所有迭代器的功能。
22. 概念可以具有类似继承的关系。概念的具体实现被称为模型。因此,指向int的常规指针是一个随机访问迭代器模型,也是一个正向迭代器模型,因为它满足该概念的所有要求。
23. STL为输出迭代器提供了ostream_iterator模板,该模板是输出迭代器概念的一个模型,它也是一个适配器——一个类或函数,可以将一些其他接口转换为STL使用的接口。
可以通过包含头文件iterator(以前为iterator.h)并作为下面的声明来创建这种迭代器:
#include <iterator>
..
ostream_iterator<int, char> out_iter(cout, "");
out_iter迭代器现在是一个接口,现在能够使用cout来显示信息。第一个模板参数(这里是int)指出了被发送给数据流的数据类型;第二个模板参数(这里为char)指出了输出流使用的字符类型。构造函数的第一个参数(这里为cout)指出了要使用的输出流,它也可以是用于文件输出的流;最后一个字符串参数是在发送给输出流的每个数据项后显示的分隔符。
可以这样使用迭代器:
*out_iter++ = 15; //work like cout << 15 << "";
对于常规指针,这意味着将15赋给指针指向的位置,然后将指针加1.但对于该ostream_iterator,这意味着将15和由空格组成的字符串发送到cout管理的输出流中,并为下一个输出操作做好准备。
可以将copy()用于迭代器,如下所示:
copy(dice.begin(), dice.end(), out_iter) //正向显示
copy(dice.rbegin(), dice.rend(), out_iter) //反向显示
这意味着将dice容器的整个区间复制到输出流中,即按照out_iter迭代器设置的输出格式显示容器的内容。
24. 三种插入迭代器
三种插入迭代器通过将复制转换为插入解决了一些问题。插入将添加新的元素,而不会覆盖已有的数据,并使用自动内存分配来确保能够容纳新的信息。另外,这三个插入迭代器都是输出容器概念的模型。
- back_insert_iterator:将元素插入到容器尾部;
- front_insert_iterator:将元素插入到容器前端;
- insert_iterator:将元素插入到insert_iterator构造函数的参数指定的位置前面;
这些迭代器将容器类型作为模板参数,将实际的容器标识符作为构造函数参数。也就是说,要为名为dice的vector< int>容器创建一个back_insert_iterator,可以这样做:
back_insert_iterator<vector<int>> back_iter(dice);
必须声明容器类型的原因是。迭代器必须使用合适容器的方法。
声明front_insert_iterator的方式与back_insert_iterator相同,对于insert_iterator声明,还需要一个指示插入位置的构造函数参数:
insert_iterator<vector<int>> back_iter(dice, dice.begin());
25. 容器种类
以前的11个容器类型分别是deque、list、quene、priority_quenue、stack、vector、map、multimap、set、multiset和bitset;C++11新增了forward_list、unprdered_map、unprdered_multimap、unprdered_set和unprdered_multiset。
26. 容器概念
容器是储存其他对象的对象。被储存的对象必须是同一种类型,它们可以是OPP意义上的对象,也可以是内置类型值。储存在容器中的数据为容器所有,这意味着当容器过期时,储存在容器中的数据也将过期(然而,如果数据是指针的话,则它指向的数据并不一定过期)。
27. 一些容器的基本特征
下表对一些通用特征进行了总结。其中X表示容器类型,如vector;T表示储存在容器中的对象类型;a和b表示类型为X的值;r表示类型为X&的值;u表示类型为X的标识符(即如果X表示vector< int >,则u的是一个vector< int>对象)。

28. 复杂度
上表中的“复杂度”一列描述了执行操作所需的时间,这个表列出了3种可能性,从快到慢依次为:编译时间,固定时间,线性时间。
如果复杂度为编译时间,则操作将在编译时执行,执行时间为0。固定复杂度意味着操作发生在运行阶段,但独立于对象种的元素数目。线性复杂度意味着时间于元素数目成正比。即如果a和b都是容器,则a==b具有线性复杂度,因为 ==操作必须用于容器的每一个元素。实际上,这是最糟糕的情况。如果两个容器的长度不同,则不需要作任何的单独比较。
29. 新增的基本容器要求
下表列出了C++11新增的通用容器要求。在这个表中,rv表示类型为X的非常量右值,如函数的返回值。

30. 序列
序列是一种重要的改进,因为7种STL容器类型(deque、C++新增的forward_list、list、queue、priority_queue、stack和vector)都是序列。
注:7种序列的容器另外详细介绍
31. 序列的要求
序列的概念增加了迭代器至少是正向迭代器这样的要求,这保证了元素将按特定顺序排列,不会在两次迭代之间发生变化。array也被归类到序列容器,虽然它并不满足序列的所有要求。
序列还要求其元素按照严格的线性顺序排列,即存在第一个元素、最后一个元素、除第一元素和最后一个元素外,每一个元素前后都分别有一个元素。
下表列出了序列的操作。其中X表示容器类型,如vector;T表示储存在容器中的对象类型;a和b表示类型为X的值;r表示类型为X&的值;u表示类型为X的标识符(即如果X表示vector< int >,则u的是一个vector< int>对象),t表示类型为T(储存在容器中的值的类型)的值,n表示整数,p、q、i和j表示迭代器。

32. 序列的可选要求
因为模板类deque、list、queue、priority_queue、stack和vector都是序列概念的模型,所以它们都支持表16.7所示的运算符。除此之外,这6个模型中的一些还可使用其他操作。在允许的情况下,它们的复杂度为固定时间。下表列出了其他操作。

33. 关于array是否为STL容器
array并非STL容器,因为其长度是固定的。因此,array没有定义调整容器的大小的操作,如push_back()和insert(),但定义了对它有意义的成员函数,如operator[ ]()和 at()。可将很多标准STL算法用于array对象,如copy()和for_each()。
34. 关联容器
关联容器是对容器概念的另一种改进。关联容器将值与键关联在一起,并使用键来查找值,键是唯一的。
STL提供了4种关联容器:set、multiset、map和multimap。
16. 6 算法
35.算法函数设计
对于算法函数设计。有两个主要的通用部分。
- 都使用模板来提供泛型;
- 都使用迭代器来提供访问容器中数据的通用表示。
16. 7 其他库
36. 三个数组模板
C++提供了三个数组模板:vector、valarray和array。这些类是由不同的小组开发的,用于不同的目的。
- vector模板类是一个容器类和算法系统的一部分,它支持面向容器的操作,如排序、插入、重新排列、搜索、将数据转移到其他容器中等。
- valarray类模板是面向数值计算的,不是STL的一部分。
- array是为替代内置数组而设计的,它通过提供更好、更安全的接口,让数组更紧凑、效率更高。
本文介绍了C++的string类、智能指针模板类、标准模板库(STL)、泛型编程、算法及其他库。详细阐述了string类的构造、输入、查找等功能,智能指针的使用及注意事项,STL的容器、算法、迭代器等概念,还提及了泛型编程的特点和迭代器类型。
学习笔记——第16章&spm=1001.2101.3001.5002&articleId=89340425&d=1&t=3&u=17e04ba678c34a4b931e1b285f5a3a12)
1431

被折叠的 条评论
为什么被折叠?



