《C++标准程序库》
——————2013/1
第五章
5.1 STL Components STL的基本观念是将数据和操作分离(与OOP观念相矛盾),数据由容器container管理,操作由算法algorithm定义,迭代器iterator充当粘合剂。
5.2 Containers 序列容器Sequence containers(可序ordered):vector、deque、list
关联容器Associative containers(已序sorted):set、multiset、map、multimap
容器适配器Container adapters:stack、queue、priority_queue
5.3 Iterator 任何容器都定义了两种迭代器型别:
Container::iterator 读/写 Container::const_iterator 只读
双向迭代器(递增++、递减--):list、set、multiset、map、multimap
随即存取迭代器(++、--、+、-):vector、deque、string
5.4 Algorithm 区间采用半开半闭,当begin() == end()容器为空
5.5 Iterator Adapter 三种迭代器适配器:
1. Insert iterator:back_inserter(container)/front_inserter(container)/inserter(container,pos)运用copy函数,目标容器空间不足可用:copy(coll1.begin(), coll1.end(), front_inserter(coll2));只要容器coll2提供有push_front()则能正常运行。
2. Stream iterator:istream_iterator、ostream_iterator
从标准输入读入容器:
copy(istream_iterator<int>(cin), istream_iterator<int>(), back_inserter(coll1));
从容器输出到标准输出:
copy(coll1.begin(), coll1.end(), ostream_iterator<int>(cout, ” ”));
3. Reverse iterator:rbegin(), rend()
5.6 Manipulating/Modifying Algorithm
(为使算法达成最大弹性,容器和数组均适用)任何“以迭代器访问容器元素”的算法,都不得(无法)透过迭代器调用容器类别所提供的任何成员函数(数组本身没有成员函数),即不必了解容器细节。 例:remove()算法删除一个容器的末尾元素,会返回一个新的指向结尾的迭代器,但是容器本身的end()却未改变,需要程序员去完成。
为此,(成员函数vs算法),应永远优先选择成员函数。
5.7 User-Defined Generic Functions
5.8 以函数作为算法的参数
1. void Print(int value);
for_each(col1.begin(), col2.end(), Print());
作用:该容器内,每个元素均调用一次Print().
2. 一元判断式:指定函数仅一个参数,返回bool类型
3. 二元判断式:指定函数有两个参数,返回bool类型
5.9 Function Object
一个类class Fun,让它重载operator(), 那么该类对象就有了函数特性,即为函数对象。
5.10 容器内的函数
1. 容器元素应满足的条件。
2. STL只支持value语义,不支持reference,这样利弊参半。只能用指针代替,但是存在一般指针的常见问题,例如比较时,比较的事指针的值,而不是指针所指的值。
5.11 STL内部的错误处理和异常处理
1. 为提高效率,STL的错误检查几乎没有。所以违反规则就会导致未定义行为。
迭代器,区间,覆盖等操作很易出错。
2. C++异常处理的一些基本保证:P139、140
第六章 STL Container
6.1 容器的共通能力和共通操作
1. 容器的元素都是value,而非reference;元素形成一个序列,即可以一次或多次遍历每个元素;传参时必须符合要求,否则引起未定义行为。
2. 容器类别的共通操作函数:P145
3. vector<int> c(istream_iterator<int>(cin), istream_iterator<int>()); 可能被当做一个函数。所以要写成 vector<int> c( ( ), ( ) );
4. 成员函数 empty() 等价于 size()==0;但前者效率更佳。
5. 赋值操作是将源容器的所有元素拷贝到目标容器,原目标容器的所有元素被删除,所以代价高昂。若源容器不再使用可采用swap()交换,它只交换内部指针,所以时间复杂度为常数。
6.2 Vector namespace std{
template <class T, class Allocator=allocator<T> >
class vector;}
1. size()当前元素个数, max_size()最大可分配的容量, capacity()动态数组当前容量, reserve()预留容量(为提高效率)
2. vector的操作函数P150、151、152、154
3. vector<bool>特例。 动态位存储,节省空间
6.3 Deque namespace std{
template <class T, class Allocator=allocator<T> >
class deque; }
1. 采用动态数组,但是和vector不一样的是 deque采用多个区块,所以内存重分配时优于vector,因为无需复制所有元素。
2. 由于第一点,所以元素的存取和迭代器的动作稍慢,需要在不同的区块间跳转,需要特殊的智能指针。
3. deque不提供容量操作capacity、reserve;但是多了在头插入删除push_front,pop_front,其它的基本和vector一样。
6.4 List namespace std{
template <class T, class Allocator=allocator<T> >
class list;}
1. list采用双向链表,不提供随机存取,只有back()和front()能直接存取,所以不提供下标操作[]和at();但在任何位置安插删除元素都非常快。
2. 提供多种成员函数P169-171
3. STL中list对异常安全性提供了最佳支持。
6.5 Set 和 Multiset
namespace std{
template <class T, class Compare=less<T>, class Allocator=allocator<T> >
class set/multiset;}
1. 排序准则默认为函数对象less<T>,可用两种方法传入排序准则:
(1)template参数定义:std::set<int, std::greater<int> > col1;
(2)以构造函数定义:set<Elem, Op> 详见P177.
2. 元素必须的排序准则:反对称(x<y真,则y<x假);可传递;非自反(x<x永为假)。
3. 只有元素类型,比较准则都相同容器才能比较,否则发生编译错误。
4. 数据结构采用:红黑树(是二叉平衡树的一种,二叉平衡树是二叉排序树的一种)
5. 因为插入元素时就自动排序,所以容器内的元素不能改变其值,而双向迭代器指向的元素都被视为常数;若要改变元素值,只能删除旧的,插入新的。
6. 多次插入删除时,一次处理完比逐一调用快得多。插入函数有一个位置参数作为插入提示,可大大加快速度。
6.6 Map 和 Multimap
namespace std{
Template< class Key, class T, class Compare = less<key>,
class Allocator = allocator<pair<const Key, T> > >
class map/multimap;}
1. map拥有set的所有能力和操作函数。set是特殊的map,key和value相等。
2. map提供下标操作符,可直接存取元素,multimap没有。若指定下标key不存在,则自动创建,而value采用该类型的默认构造函数。
3. 所有元素的key被视为常数。若要修改key,而不改变value,可采用一个比较方便的方法:map1[“new_key”]=map1[“old_key”]; map1.erase(“old_key”);
4. 实例:关联式数组p207、字典p209。
5. 综合实例:如何撰写使用function object;如何在执行期定义排序准则;如何在“不在乎大小写”的情况下比较字符串(toupper();)。 P213
6.7 其他STL容器
1. STL是一个框架。使用其它数据结构定义容器的三种不同方法:P217
(1)侵入式:直接提供STL所需接口,如begin(),end()之类的常用函数。这种方法是以某种特定方式编写容器,所以叫侵入式。
(2)非侵入式:只提供特殊的迭代器作为算法与特殊容器的界面,只需遍历容器所有元素的能力。
(3)包装法:上述两种方法的组合,将一个数据结构包装出于STL容器相似的接口。
2. 将数组包装成array容器的实例。 P219
6.8 动手实现reference语义
使用引用计数的智能指针作为容器的元素。
6.9 各容器的运行时机
P226
6.10 细说容器内的型别和成员
1. 容器内的型别:reference, const_reference, iterator, const_iterator, reverse_iterator, const_reserve_iterator, size_type, difference_type, value_type, key_type, mapped_type, key_compare, value_compare, allocator_type.
2. Constructor, Copy, Destructor : P231
3. Nomodifying Operations : P233
4. Assignments: operator=, assign(), swap().
5. 返回元素:front(), back(), at(), operator[].
返回迭代器:begin(), end(), rbegin(), rend().
6. Inserting 、 Removing : P240
7. Lists的特殊成员函数:unique(), splice(), merge(), sort(), reverse().
8. STL容器的异常处理: P249
第七章 STL Iterator
7.1 迭代器头文件: <iterator>容器本身已包含该头文件。
7.2 迭代器类型: Input,Output、Forward、Bidirectional、Random access
7.3 迭代器相关辅助函数:
//使迭代器前进n步,n可以为负数
1. #include<iterator> void advance(InputIterator& pos, Dist n);
//返回两迭代器之间的距离
2. #include<iterator> Dist distance(InputIterator pos1, InputIterator pos2);
//交换两个迭代器所指元素的内容
3. #include<algorithm> void iter_swap(ForwardIterator pos1, ForwardIterator pos2);
7.4 迭代器适配器
1. 逆向迭代器:逆向迭代器指向的是实际元素位置,返回的是所指位置的下一跳的值。
例:begin()、rend()所指位置相同,但是rend()取值返回的是begin()所指位置的前一个;end()、rbegin()所指位置相同,但是rbegin()取值返回的是end()所指的起一个元素。
逆向迭代器直接用一般迭代器初始化:pos=v.begin(); vector<int>::reverse_iterator rpos(pos);
逆向迭代器转化为正向迭代器使用成员函数:pos = rpos.base();
2. 安插型迭代器:
名称 |
Class |
其所调用的函数 |
生成函数 |
Back inserter |
back_insert_iterator |
push_back(value) |
back_inserter(coll) |
Front inserter |
Front_insert_iterator |
push_back(value) |
front_inserter(coll) |
General inserter |
Insert_iterator |
insert(pos, value) |
inserter(coll, pos) |
3. 流迭代器
Ostream迭代器:template ... class ostream_iterator;
Istream迭代器: template ... class istream_iterator;
所有istream迭代器只要任何一次读取失败都会变成end-of-stream,所以每进行一次读取都都应将istream迭代器与end-of-stream(istream迭代器的default构造函数生成)比较,检查这个迭代器是否合法。
7.5 迭代器特性
template <class T>
struct iterator_traits {
typedef typename T::value_type value_type;
typedef typename T::difference_type difference_type;
typedef typename T::iterator_category iterator_category;
typedef typename T::pointer pointer;
typedef typename T::reference reference;
};
第八章 STL Function objects
8.1 函数对象(或叫仿函数functor)的概念: 定义了operator()的对象。
函数对象比一般函数更灵巧,因为它可以拥有状态,而且不止一个状态;
函数对象是class,有型别的。所以可以将它当做template参数传递;
执行速度上函数对象通常比一般函数快。
函数对象做函数参数传递时是passed by value,而不是reference!若需传递reference则要对template参数进行特化:
generate_n<back_insert_iterator<list<int> >, int, Functor&>( back_inserter(coll), 4, seq);
8.2 预定义的函数对象 P305#include<functional>
1. 函数适配器:指能将两个函数对象结合起来的函数对象(函数复合)。bind1st, bind2nd, not1, nos2
2. mem_fun_ref, men_fun(不能直接将一个成员函数传给一个算法,需要运用适配器,这两者调用的成员函数必须是const)
3. ptr_fun(针对非成员函数)
8.3 辅助用(综合型)函数对象:实现多个函数对象的组合使用,如:A and B
1. 一元组合函数适配器
2. 二元组合函数适配器
第九章 STL Algorithm
第十章 STL Special Containers
10.1 Stack
Namespace std {
Template <class T, class Container = deque<T> >
Class stack; }
核心接口:push() top() pop()
10.2 Quene
Namespace std {
Template <class T, class Container = deque<T> >
Class queue; }
核心接口:push() front() pop() back()
10.3 Priority Queue
Namespace std {
Template <class T, class Container = vector<T>,
Class comjpare = less<typename Container::value_type> >
Class priority_queue; }
核心接口:push() top() pop()
10.4 Bitset
Bitset是容量不可变得位集,vector<bool>则是动态位集。
Namespace std {
Template <size_t Bits>
Class bitset; }