C++ Primer Plus(十六)——string类和标准模板库

1. string类将string::npos定义为字符串的最大长度,通常为unsigned int的最大值

2. string类的构造函数有一个模板参数:

template<class Iter> string(Iter begin, Iter end);

begin,end像指针那样,指向内存中的两个位置。通常,begin和end可以是迭代器——广泛用于STL的广义化指针。构造函数将使用begin和end指向的位置之间的值,对string对象进行初始化,包括begin,不包括end,end指向的是被使用的最后一个值后面的一个位置。

3. 输入C-风格字符串

(1)对于类,有三种方式:

char info[100];
cin >> info;
cin.getline(info, 100);
cin.get(info, 100);

(2)对于string对象,有两种方式:

string stuff;
cin >> stuff;
getline(cin, stuff);

在功能上,它们之间的主要区别是string版本的getline( )将自增string对象大小;在设计方面的区别是,读取C-风格字符串的函数是istream的方法,而string版本是独立的函数

(1) string版本的getline( )函数从输入中读取字符,并将其存储到目标string中,直到发生下列三种情况:

  a. 到达文件尾,此时的eofbit将被重置,这意味着fail( )和eof( )都将返回true

  b. 遇到分界字符(默认为\n),此时将把分界字符从输入流中剔除,但不存储它

  c. 读取的字符数达到最大允许值(string::npos和可供分配的内存字节数中较小的一个),此时将设置输入流的failbit,这意味着fail( )将返回true

输入流对象有一个统计系统,用于跟踪流的错误状态。在这个系统中,检测到文件尾后将设置eofbit寄存器,检测到输入错误时将设置failbit寄存器,出现无法识别的故障将设置badbit等寄存器,一切顺利时将设置goodbit寄存器。

(2) string版本的operator>>( )函数的行为与此相似,只是它不断读取,直到遇到空字符串将其留在输入队列中,而不是不断读取,直到遇到分界字符将其丢弃。空白字符指的是空格、换行符、制表符,是任何将其作为参数来调用isspace( )时,函数返回true的字符。

4. 字符串查找:

(1)find_first_of:在字符串中查找参数中任何一个字符首次出现的位置。

(2)find_last_of:同上,这是它查找的是最后一次出现的位置。

(3)find_first_not_of:查找第一个不包含在参数中的字符

(4)rfind:查找子串或者字符最后一次出现的位置

5. 智能指针不能用于非堆内存

6. 避免设计的智能指针删除同一个对象两次有以下方法:

(1)定义赋值运算符,使之进行深复制。这样两个指针将指向不同对象,其中的一个对象是另一个对象的副本。

(2)建立所有权概念,对于特定对象,只有一个智能指针能拥有它,这样只有拥有对象的智能指针的构造函数可以删除该对象。然后,让赋值操作转让所有权。这就是用于auto_ptr和unique_ptr的策略,但unique_ptr的策略更为严格。程序试图将unique_ptr赋值给另一个时,如果源unique_ptr是个临时右值,编译器允许这么做;如果源unique_ptr将存在一段时间,编译器将禁止这么做。而auto_ptr允许这两种赋值。

(3)创建智能更高的指针,跟踪引用特定对象的智能指针数,也就是引用计数(reference counting)。这是shared_ptr所采用的策略。

7. 使用new分配内存时,才能使用auto_ptr和shared_ptr,使用new[ ]分配内存时,不能使用它们。不使用new分配内存时,不能使用auto_ptr或shared_ptr;不使用new或new[ ]分配内存时,不能使用unique_ptr。

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

9. 如果编译器没有unique_ptr,可考虑使用Boost库的scoped_ptr,它与unique_ptr相似。

10. 即使有执行相同任务的非成员函数,STL有时也会定义一个成员函数。这是因为对有些操作来说,特定算法的效率比通用算法高。

11. for_each函数将被指向函数应用于容器区间中的各个元素,被指向函数不能修改容器元素的值,可部分代替for循环。

12. Randon_shuffle( )函授接受两个指定区间的迭代器参数,并随机排列该区间中的元素,但要求容器类允许随机访问,sort函数也要求容器支持随机排序。

13. 泛型编程,对于函数,不仅要使其独立于容器中存储的数据类型,而且要独立于容器本身的数据结构。模板提供了存储在容器中的数据类型的通用表示,而迭代器提供了容器中值的通用表示。

14. 为区分++运算符的前缀版本和后缀版本,C++将operator++作为前缀版本,将operator++(int)作为后缀版本,其中的参数永远不会用到,所以不必指定其名称。

15. 总结STL方法:首先是处理容器的算法,应尽可能用通用的术语来表达算法,使之独立于数据类型和容器类型。为使通用算法能够适用于具体情况,应定义能够满足算法需求的迭代器,并把要求加到容器设计上。基于算法的要求,设计基本迭代器的特征和容器特征。

16. 输入迭代器(InputIterator)并不能保证迭代器第二次遍历容器时,顺序不变。输入迭代器递增后,也不能保证先前的值仍然可以被解除引用。基于输入迭代器的任何算法都应当是单通行的,不依赖于前一次遍历时的迭代器值,也不依赖于本次遍历中前面的迭代器值。也就是说,输入迭代器是单项迭代器,可以递增,但不能倒退。

17. 对于单通行、只写算法,则可以使用输出迭代器

18. 正向迭代器与输入迭代器和输出迭代器类似,不同的是它只使用++运算符来遍历容器,总是按照相同的顺序遍历一系列值,将正向迭代器递增后,仍然可以对前面的迭代器解除引用,并可以得到相同的值。

19. 双向迭代器具有正向迭代器的所有特性,同时支持两种递减运算符。

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

21. 迭代器是广义指针,而指针可以满足所有的迭代器需求。迭代器是STL算法的接口,而指针也是迭代器,因此STL算法可以使用指针来对基于指针的非STL容器进行操作。

22. ostream_iterator是输出迭代器概念的一个模型,它也是一个适配器,可以将一些其他的接口转换为STL使用的接口

template< class T, // 被发送给输出流的数据类型
          class CharT = char, //输出流使用的字符类型
          class Traits = std::char_traits<charT>
        >
class ostream_iterator;

// 构造函数 :1. 要使用的输出流 2.每个数据项后显示的分隔符
ostream_iterator(ostream_type& stream, const CharT* delim) 
ostream_iterator(ostream_type& stream)

23. istream_iterator是输入迭代器概念的模型。

template< class T, // 要读取的数据类型
          class CharT = char, // 输入流使用的字符类型
          class Traits = std::char_traits<CharT>,
          class Distance = std::ptrdiff_t >
class istream_iterator;

// 构造函数 : 要管理的输入流
istream_iterator( istream_type& stream );
istream_iterator( const istream_iterator& other ) = default;

24. reverse_iterator反向迭代器概念的模型。

template< class Iterator >
class reverse_iterator : public std::iterator<
                           typename std::iterator_traits<Iterator>::iterator_category,
                           typename std::iterator_traits<Iterator>::value_type,
                           typename std::iterator_traits<Iterator>::difference_type,
                           typename std::iterator_traits<Iterator>::pointer,
                           typename std::iterator_traits<Iterator>::reference >

25. copy函数不能自动根据发送值调整目标容器的长度,如果复制的长度大于容器本身长度,则程序可能会因为内存违规而异常终止,而back_insert_iterator、front_insert_iterator、insert_iterator通过将复制转换为插入解决了问题,插入将添加新的元素,而不会覆盖已有的数据,并使用自动内存分配来确保能够容纳新的信息。

26. 容器种类

C++11(前): deque、list、queue、priority_queue、stack、vector、map、multimap、set、multiset、bitset

C++11(新增): forward_list、unordered_map、unordered_multimap、unordered_set、unordered_multiset

27. 容器的基本特征

表达式返回类型说明复杂度
X::iterator指向T的迭代器类型满足正向迭代器要求的任何迭代器编译时间
X::value_typeTT的类型编译时间
X u 创建一个名为u的空容器固定
X( ) 创建一个匿名的空容器固定
X u(a) 调用复制构造函数后 u == a线性
X u = a 同上线性
r = aX&调用赋值构造函数后 r == a线性
(&a)->~X( )void对容器中的每个元素应用析构函数线性
a.begin( )迭代器返回指向容器第一个元素的迭代器固定
a.end( )迭代器返回超尾值迭代器固定
a.size( )无符号整型返回元素个数固定
a.swap(b)void交换a,b内容固定
a == b / a != b可转换为bool比较a和b的元素是否完全相同/不相同线性

编译时间(执行时间为0) < 固定时间(独立于元素数目) < 线性时间

28. C++11 新增的容器基本特性

表达式返回类型说明复杂度
X u(rv) 调用移动构造函数后,u值与rv的原始值相同线性
 X u=rv 同上线性
a=rvX&调用移动赋值运算符后,u值与rv的原始值相同线性
a.cbegin()const_iterator返回指向容器第一个元素的const 迭代器固定
c.cend()const_iterator返回超尾值const迭代器固定

29. 复制构造和复制赋值与移动构造和移动赋值之间的差别在于,复制操作保留源对象,而移动操作可以修改源对象,还可能转让所有权,而不作任何复制。如果源对象是临时的,移动操作的效率高于常规复制。

30. 序列:deque、queue、priority_queue、stack、vector、list、C++11新增的forward_list。序列概念增加了迭代器至少是正向迭代器的要求,这保证了元素按照一定顺序排列,不会在两次迭代之间发生变化。下表列出了序列必须完成的操作,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( ))

序列的可选要求

表达式返回类型含义容器
a.front( )T&*a.begin( )vector、list、deque
a.back( )T&*--a.end( )vector、list、deque
a.push_front(t)voida.insert(a.begin( ), t)list、deque
a.push_back(t)voida.insert(a.end( ), t)vector、list、deque
a.pop_front( )voida.erase(a.begin( ))list、deque
a.pop_back( )voida.erase(--a.end())vector、list、deque
a[n]T&*(a.begin() + n)vector、deque
a.at(n)T&*(a.begin() + n)vector、deque

(1)vector : 序列+可反转容器,适用范围——快速随机访问

(2)deque:双端列表,适用范围——如果多数操作发生在序列的起始和结尾处,执行速度——vector 快于 deque

(3)list:双向链表,序列+可反转容器,适用范围——元素的快速插入和删除

list成员函数

函数说明复杂度
void merge(list<T, Alloc>& x)将x与调用链表合并,两个链表必须已经排序。合并后的已经经过排序的链表保存在调用链表中,x为空线性
void remove(const T& val)从链表删除val的所有实例线性
void sort( )使用<运算符对链表进行排序N个元素的复杂度为NlogN
void splice(iterator pos, list<T, Alloc> x)将链表内容插入到pos的前面,x将为空固定
void unique( )将连续相同的元素压缩为单个元素线性

(4)forward_list(c++11):单链表 + 不可反转容器

(5)queue:队列+适配器类,不允许随机访问队列元素、不允许遍历队列,只允许empty/size/font/back/pop/push操作,底层为deque

(6)priority_queue :队列+适配器类,支持的操作与queue相同,区别是该队列把最大的元素移到队首,底层为vector

(7)stack:栈+适配器类,不允许随机访问栈元素、不允许遍历栈,支持的操作为empty/size/top/pop/push,底层为vector

(8)array(C++11)

31. 关联容器:set,multiset,map,multimap,将值与键关联在一起,并使用键来查找值。关联容器的优点在于它提供了对元素的快速访问。关联容器也允许插入元素,但不能指定元素的插入位置,原因是关联容器通常有用于确定数据放置位置的算法,以便能快速检索信息。

(1)set:可反转,可排序,键值唯一。数学为集合定义了一些标准操作,STL提供了支持这些操作的算法,它们是通用函数,而不是方法,使用算法的先决条件是——容器是经过排序的。set_union函数接受5个迭代器参数,前两个迭代器定义了第一个集合的区间,接下来的两个迭代器定义了第二个集合的区间,最后一个迭代器是输出迭代器,指出将结果集合复制到什么位置。若要将结果放到集合C中,而不是显示它,则最后一个参数应该是指向C的迭代器,显而易见的选择是C.begin(),但它不管用,因为C.begin返回的是常量迭代器,不能用作输出迭代器,而且set_union操作将覆盖容器中的已有数据,并要求容器有足够的空间容纳新信息,C是空的话就不满足这个需求。正确用法如下:

set_union(A.begin(), A.end(), B.begin(), B.end(), insert_iterator<set<string> >(C, C.begin())

函数set_intersection和set_difference分别查找交集和获得两个集合的差,接口与set_union相同。set有两个有用的方法lower_bound和upper_bound,lower_bound将键作为参数并返回一个迭代器,该迭代器指向集合中第一个不小于键参数的成员,upper_bound将键作为参数并返回一个迭代器,该迭代器指向集合中第一个大于键参数的成员。

(2)multimap:可反转,可排序,成员函数equal_range用键作为参数。返回两个迭代器,分别等于lower_bound和upper_bound的值

32. 无序关联容器:与关联容器的差别在于,关联容器是基于树结构的,而无序关联容器是基于哈希表的,有4种无序关联容器,分别是unordered_set,unordered_multiset,unordered_map,unordered_multimap。

33. 函数符是可以以函数方式与( )结合使用的任意对象,包括函数名、指向函数的指针和重载了()运算符的类对象:

(1)生成器(generator):不用参数就可以调用的函数符

(2)一元函数(unary function):一个参数就可以调用的函数符

(3)二元函数(binary function):两个参数就可以调用的函数符

(4)谓词(predicate):返回bool值的一元函数

(5)二元谓词(binary predicate):返回bool值的二元函数

34. 所有内置的算术运算符、关系运算符、逻辑运算符,STL都提供了等价的函数符:

运算符和相应的函数符

运算符相应的函数符
+plus
-minus
*times(old)/multiplies(new)
/divides
%modulus
-negate
==equal_to
!=not equal to
>greater
<less
>=greater euqal
<=less equal
&&logical and
| |logical or
!logical not

35. STL使用binder1st和binder2nd将自适应二元函数转为自适应一元函数

36. STL将算法库分成4组:非修改式序列操作、修改式序列操作、排序和相关操作、通用数字计算,前三组在头文件 algorithm(旧版本为algo)中描述,第四组在numeric中描述。

37. STL约定,就地算法,将结果存放在原始数据的位置上;复制算法将结果发送到另一个位置,以_copy结尾,并接受一个额外的输出迭代器参数,返回一个迭代器,该迭代器指向复制的最后一个值的后一个位置。有些函数根据将函数应用于容器元素得到的结果来执行操作,这些函数通常以_if结尾。

38. STL使用Predicate来提醒用户,实参应该模拟Predicate概念,使用Generator和BinatyPredicate来指示必须模拟其它函数对函数概念的参数。虽然文档指出迭代器和函数符需求,但编译器不会对此进行检查。如果使用了错误的迭代器,编译器试图实例化模板时,将显示大量的错误信息。

39. next_prermutation( )算法,自动提供唯一的排列组合,将区间内容转换为下一种排列方式,如果成功,算法返回true,如果区间处于最后的序列中,算法返回false。

40. STL的remove算法,不是函数成员,在调整链表(如果Iterator指向的是链表)时,将没被删除的元素放在链表的开头,并返回一个指向新超尾值的迭代器,该迭代器到旧超尾值的迭代器的区间,就是不需要的部分。

41. C++为何提供三个数组模板:vector,valarray,array?

(1)vector模板类是容器类和算法系统的一部分,它支持面向容器的操作

(2)valarray类模板是面向数值计算的,不是STL的一部分。它重载了各种组合赋值运算符,重载了各种数学函数,使之接受一个valarray参数,并返回一个valarray对象,还提供了apply(不修改对象,而是返回一个包含结果的新对象),sum,size,max,min方法。虽然valarray接口提供了resize方法,但不能像vector使用push_back一样自动调整大小,也没有插入删除排序搜索等操作的方法,这使得它的接口更加简单,但接口更简单并不意味着性能更好,简单表示法通常是使用类似循环实现的,虽然可能有些硬件可以通过同时将一个数组中的值加载到一组寄存器中并并行的处理实现。valarray没有定义超过下标超尾尾部的行为,这可能出现不可预知的风险。为解决这些问题,C++11提供了接受valarray对象作为参数的模板函数begin( )和end( )。valarray只定义了与单个int元素的操作,如果需要多个元素的操作(例如slice),则需要使用slice指定的元素来创建一个完整的valint对象,如valint[slice(1,4,3)]

(3)Array是为替代内置数组而设计的,表示固定的数组,他通过提供更好、更安全的接口,让数组更紧凑、效率更高,不支持push_back、insert,但提供了多个STL方法,包括begin、end、rbegin、rend,这使得很容易将STL算法用于array对象。

42. 扩展下标的指定版本——slice类,它可以被用作数组索引。slice对象被初始化为三个整数值:起始索引、索引数、跨距,起始索引是第一个被选中的元素的索引,索引数指出要选择多少元素,跨距表示元素之间的间隔。

43.  模板initializer_list之所以可行,是因为容器类包含将initializer_list<T>作为参数的构造函数。如果类和容器都有接受initializer_list作为参数的构造函数,则使用语法{}调用类的构造函数,并将该容器存储进容器。所有initializer_list元素的类型必须相同,但编译器将进行必要的转换,但不能进行隐式的窄化转换。要在代码中使用initializer_list对象,必须包含头文件initializer_list,它包含成员函数begin( )、end( )和size( ),initializer_list的迭代器类型为const,因此不能修改initializer_list中的值。

转载于:https://my.oschina.net/shou1156226/blog/1527817

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值