Chapter16 string类和标准模板库

Chapter16 string类和标准模板库

16.1 string类

16.1.1 构造字符串

string类将string::npos定义为字符串的最大长度,通常为unsigned int的最大值,下表中NBTS(null-terminated string)表示以空字符结束的字符串,即传统的c字符串。
image-20201108133932793

16.1.2 string类输入

对于C-风格字符串,有三种输入方式:

1
2
3
4
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对象,有两种方式:

1
2
3
string stuff;
cin >> stuff; //read a word
getline(cin, stuff); //read a line, discard \n

两个版本的getline()都有一个可选参数,用于指定使用哪个字符来确定输入的边界:

1
2
cin.getline(info, 100, ':'); //read up to :, discard :
getline(stuff, ':'); //read up to :, discard :

在功能上,两者的主要区别在于,string版本的getline()将自动调整目标string对象的大小,使之刚好能够存储输入的字符。在设计方面的区别是,读取C-风格字符串的函数是istream类的方法,而string版本是独立的函数。对于C-风格字符串输入,cin是调用对象;而对于string对象输入,cin是一个函数参数。

string版本的getline()函数从输入中读取字符,并将其存储到目标string中,直到发生下列三种情况之一:
(1)到达文件尾,在这种情况下,输入流的eofbit将被设置,这意味着方法fail()和eof()都将返回true;
(2)遇到分界字符(默认为\n),在这种情况下,将把分界字符从输入流中删除,但不存储它;
(3)读取的字符数达到最大允许值(string::npos和可供分配的内存字节数中较小的一个),在这种情况下,将设置输入流的failbit,这意味着fail()将返回true。

16.1.3 使用字符串

size()和length()成员函数都返回字符串中的字符数,length()成员来自较早版本的string类,而size()则是为提供STL兼容性而添加的。

image-20201108142109286

string库还提供了相关的方法:rfind()、find_first_of()、find_last_of()、find_first_not_of()和find_last_not_of(),它们的重载函数特征标都与find()方法相同。
rfind()方法查找子字符串或字符最后一次出现的位置;
find_first_of()方法在字符串中查找参数中任何一个字符首次出现的位置;
find_last_of()方法在字符串中查找参数中任何一个字符最后一次出现的位置;
find_first_not_of()方法在字符串中查找第一个不包含在参数中的字符。

string库实际上是基于一个模板类的:

1
2
3
4
5
6
template <class charT, class traits = char _traits<charT>, class Allocator=allocator<charT>>
basic_string{};//有四个具体化,每个具体化都有一个typedef名称
typedef basic_string<char> string;
typedef basic_string<wchar_t> wstring;
typedef basic_string<char16_t> u16string;//c++11
typedef basic_string<char32_t> u32string;//c++11

16.2 智能指针模板

auto_ptr、unique_ptr和shared_ptr这三个智能指针模板都定义了类似指针的对象,可以将new获得的地址赋给这种对象。当智能指针过期时,其析构函数将使用delete来释放内存。

要创建智能指针对象,必须包含头文件memory,然后使用通常的模板语法来实例化所需类型的指针。

所有智能指针类都有一个explicit构造函数,该构造函数将指针作为参数。

解决删除同一对象两次的方法:
(1)定义赋值运算符,使之进行深复制,这样两个指针将指向不同的对象。
(2)建立所有权(ownership)概念,对于特定的对象,只能有一个智能指针可拥有它,这样只有拥有对象的智能指针的构造函数会删除该对象。(unique_ptr)
(3)创建智能更高的指针,跟踪引用特定对象的智能指针书(引用计数,shared_ptr采用的策略)

若程序试图将一个unique_ptr赋给另一个,如果源unique_ptr是个临时右值,编译器允许这样做;如果源unique_ptr将存在一段时间,编译器将禁止这样做。

如果程序要使用多个指向同一对象的指针,应选择shared_ptr。

模板shared_ptr包含一个显式构造函数,可用于将右值unique_ptr转换为shared_ptr,shared_ptr将接管原来贵unique_ptr的所有对象。

16.3 标准模板库

STL提供了一组表示容器、迭代器、函数对象和算法的模板。容器是一个与数组类似的单元,可以存储若干个值。STL容器是同质的,即存储的值的类型相同;算法完成特定任务的处方;迭代器能够用来遍历容器的对象,与能够遍历数组的指针类似,是广义指针;函数兑现格式类似于函数的对象,可以是类对象或函数指针。

所有的STL容器都提供了一些基本方法,其中包括size()——返回容器中的元素数目、swap()——交换两个容器的内容、begin()——返回一个指向容器中第一个元素的迭代器、end()——返回一个表示超过容器尾(指向容器最后一个元素后面的哪个元素)的迭代器。

每个容器类都定义了一个合适的迭代器,该迭代器的类型是一个名为iterator的typedef,其作用域为整个类。

vector类方法:
push_back()将元素添加到矢量末尾。
erase()方法删除矢量中给定区间的元素。它接受两个迭代器参数,这些参数定义了要删除的区间。
insert()方法接受3个迭代器参数,第一个参数制定了新元素的插入位置,第二个和第三个迭代器参数定义了被插入区间,该区间通常是另一个容器对象的一部分。

对有些操作来说,类特定算法的效率比通用算法高。

for_each()函数接受3个参数,前两个是定义容器中区间的迭代器,最后一个是指向函数的指针(更普遍的说最后一个参数是一个函数对象)。for_each()函数将被指向的函数应用于容器区间中的各个元素。被指向的函数不能修改容器元素的值。

Random_shuffle()函数接受两个指定区间的迭代器参数,并随机排列该区间中的元素(要求容器类允许随机访问)。

16.4 泛型编程

面向对象编程关注的是编程的数据方面,而泛型编程关注的是算法。

模板使得算法独立于存储的数据类型,而迭代器使得算法独立于使用的容器类型。

  • 输入迭代器

    输入迭代器可被程序用来读取容器中的信息。基于输入迭代器的任何算法都应当是单通行(single-pass)的,不依赖于前一次遍历时的迭代器值,也不依赖于本次遍历中前面的迭代器值。

  • 输出迭代器

    程序输出就是容器的输入。

    输出迭代器于输入迭代器相似,只是解除引用让程序能够修改容器值,而不能读取

    对于单通行、只读算法,可以使用输入迭代器;而对于单通行、只写算法,则可以使用输出迭代器

  • 正向迭代器

    正向迭代器只是用++运算符莱遍历容器,所以它每次沿容器向前移动一个元素,与输入和输出迭代器不同的是,它总是按相同的顺序遍历一系列值。将正向迭代器递增后,仍然可以对前面的迭代器值接触引用,并可以得到相同的值

  • 双向迭代器

    双向迭代器具有正向迭代器的所有特性,同时支持两种(前缀和后缀)递减运算符

  • 随机访问迭代器

    随机访问迭代器具有双向迭代器的所有特性,同时添加了支持随机访问的操作和用于对元素进行排序的关系运算符。

image-20201108153205759

16.5 函数对象

函数对象也叫函数符(functor),是可以以函数方式与()结合使用的任意对象。

函数符概念:

  • 生成器(generator)是不用参数就可以调用的函数符。
  • 一元函数(unary function)是用一个参数可以调用的函数符。
  • 二元函数(binary function)是用两个参数可以调用的函数符。
  • 返回bool值的一元函数是谓词(predicdate).
  • 返回bool值的二元函数是二元谓词(binary predicate)。

image-20201108153857841