<<C++标准程序库>>中的STL简单学习笔记

0. 内容为个人学习笔记, 仅供参考, 如有错漏, 欢迎指正!

1. STL中的所有组件都是由模板构成的, 所以其元素可以是任意型别的. 组件有:

  - 容器: 管理某类对象的集合. 不同的容器有各自的优缺点.

  - 迭代器: 用来在一个对象集群(Collection of Objects) 的元素上进行遍历. 这个CoB可以是容器/容器的一部分. 每种容器都提供了自己的迭代器.

  - 算法(Algorithm): 用来处理集群内的元素(比如: 查询,修改,排序等).

  - 适配器(adapter)

  - 仿函数(functors or function objects)

2. STL基本观念是将数据和操作分离, 数据交给容器进行管理, 操作(算法)可以定制算法, 使用迭代器来提供两者间统一的接口.

  - STL 的概念有点儿和OOP的最初思想矛盾, 然而这个小框架提供了很好的容器/算法弹性.

  - STL 是泛型编程的出色范例, 容器和算法对任意型别和类别而已已经一般化了.

  - STL 提供了适配器和仿函数可以实现对算法的: 补充, 约束 和定制, 从而满足不同的需求.

3. 容器

  - 序列式容器(Sequence container): 可序集群.(3个)  ---- vector/deque/list (为什么queue,stack 和 string不是呢?)

    - 可以将string 和 array 也当作是一种序列式容器(why?)

    - array 是C/C++语言核心所支持的一个型别(type), 而不是(class). 具有静态大小和动态大小. 但它也并不是STL容器. 因为没有类似size(),empty()等成员函数. 但是STL的设计又允许它调用STL算法. 当我们以static arrays作为初始化行时特别有用.

    - 没有必要直接编写dynamic array了, 因为可以用vector实现.

  - 关联式容器(Association container): 已序集群(4个). 排序和元素值,插入顺序无关. ---- set/multiset/map/multimap

    - 自动会对元素进行排序. 默认情况下是使用 operator < 进行比较.

    - 一般关联容器由二叉搜索树实现 , 各容器的主要差别在于元素的类型已经处理重复元素的方式.

    - set: 按内部元素的值进行排序, 不允许重复.

    - multiset: 和set相同, 但允许重复

    - map: key/values , 键不允许重复.

    - multimap: 和map一样, 但键允许重复.可以用来当做字典.

4. 容器适配器

  - stack: FILO

  - queue: FIFO

  - priority_queue: 下一个出队的元素永远是具有最高优先级的元素  ( 默认比较是 operator < 所以数值越小,优先级越高? ).

5. 迭代器(基本操作)

  - operator */++/==/!=/=

  - 每种容器都必须提供自己的内部类, 事实上每种容器都将其适配器定义为内部类.

  - 任何一种容器都有2种类别的迭代器

    - container::iterator

    - container::const_iterator

 6. 关联式容器的一些应用

  -  typedef set<int> IntSet;  /  typedef set<int, greater<int> >IntSet;  greater<> 是一个预先定义的仿函数.

  - 所有的关联容器都有一个insert()方法, 但是没有序列式容器的push_back(),push_front()方法 , 因为你没有权力指定新元素的位置.

  - map 允许使用  operator []  来安插元素. 而multimap不允许使用下标操作符(因为key可以重复哦).

  - 存取multimap或map的元素时, 必须用pair结构的first和second成员才能访问到具体key/value

 1 #include <iostream>
 2 #include <map>
 3 #include <string>
 4 using namespace std;
 5 int main(){
 6     typedef map<string,float> StringFloatMap;
 7     StringFloatMap coll;
 8     coll["VAT"] = 0.15;
 9     coll["pi"] = 3.14;
10     coll["This is a string"] = 3.4;
11     coll["NULL"] = 0;
12     StringFloatMap::iterator it = coll.begin(); // map 是不能手动排序,但是并不是没有序
13     for(;it!=coll.end();++it){ // 使用++it效率高于 it++
14         cout<<it->first<<" : "<<it->second<<endl;
15     }
16     return 0;
17 }
View Code

7. 迭代器分类(2种)

  - 双向迭代器(Bidirectional iterator): list/set/multiset/map/multiset提供的属于此类.

  - 随机存取迭代器(Random access iterator): vector/deque/string提供的属于此类. 随机访问迭代器可以支持  operator < 

8. 算法

  - 不是容器的成员函数, 而是搭配迭代器使用的全局函数. 

  -  pos = min_element(coll.begin(),coll.end())   /   pos = max_element(coll.begin(),coll.end())  

  -  sort(coll.begin(),coll.end()[,comp]); 

  -  pos = find(coll.begin(),coll.end(),value); 

  -  reserve(pos,coll.end()) 

  - 处理多个区间

    -  equal(coll1.begin(),coll1.end(), coll2.begin() ); //必须设定第一个区间的起点和终点, 至于其他区间, 只需设置起点即可, 终点通常由第一区间的元素数量推断出来.  

    -  copy(coll1.begin(),coll1.end(),coll2.begin()); //处理多个区间时注意区间的大小, 否则可能会读写到其他地方造成错误,如果不是STL安全版本不会有异常. 

    - 可以使用resize()改变目标容器的大小, 适用于序列式容器.

9. 迭代器适配器(Iterator Adapters)

  - 迭代器是接口, 任何类别(class)只要具备这个接口就可以实现迭代.  C++标准库提供了几个预定义的特殊迭代器, 即迭代器适配器(以下3种).

  - Insert Iterator: 可以使算法以插入而非覆写的方式运作. 插入的位置(最前/最后/特定位置), 根据3重不同的Insert Iterator而异.

    - back_inserter: copy(coll1.begin(),coll1.end(),back_inserter(coll2)); 

      - 只有在提供push_back()的容器中才能用. (vector/deque/list)

    - front_inserter:  copy(coll1.begin(),coll1.end(),front_inserter(coll3)); 

      - 容器要满足有push_front(). (deque/list)

    - inserter:  copy(coll1.begin(),coll1.end(),inserter(coll2,coll2.begin()); //只有inserter可以在关联容器上工作(非关联容器上应该也可以吧). 上面两种不行. 

      - 所有的STL容器都提供有insert(), 对于关联式容器而言, 提供一个开始搜寻的位置. 

  - Stream Interator: 一种用来读写流的迭代器, 提供了必要的抽象性, 使得来自键盘的输入像是个集群.

    -  copy(istream_iterator<string> (cin), istream_iterator<string>(), back_inserter(coll)); 

      - istream_iterator<string>(cin) : 会产生一个从标准输入流cin读取数据的stream Iterator,读取string类型, 每当算法处理下一个元素时, 就转化为:cin>>string

      - ostream_iterator<string>(): 调用istream iterator的default构造函数, 产生一个代表"流结束符"的迭代器. 

    -  unique_copy(coll.begin(),coll.end(),ostream_iterator<string>(cout," ")); 

      - 第二个参数是可选的元素分隔符.

  - Reverse Iterator:  提供逆向操作. 可以通过 rbegin()/rend()来产生逆向迭代器.

    -  copy(coll.rbegin(), coll.rend(), ostream_iterator<int>(cout," ")); 

    - 可以将一般的迭代器转换为逆向迭代器, 反之亦然.

10. 更易型算法(manipulating algorithm)

  - remove(): 移除元素, 但是容器的size(),返回的end() 没有改变, 因为是用容器后面的元素覆盖了删除的元素.

  - remove()会返回删除元素后新的迭代器的位置, 而不改变原来容器迭代器的位置.  list<int>::iterator end = remove(coll.begin(),coll.end(),value); 

 1 #include <iostream>
 2 #include <map>
 3 #include <string>
 4 #include <iterator>
 5 #include <list>
 6 #include <algorithm>
 7 using namespace std;
 8 int main(){
 9     list<int> coll; //list提供双向迭代器, 是双向链表.
10     for(int i=1;i<=6;++i){
11         coll.push_front(i);
12         coll.push_back(i);
13     }
14     copy(coll.begin(),coll.end(), ostream_iterator<int>(cout," "));
15     cout<<endl;
16     list<int>::iterator end = remove(coll.begin(),coll.end(),3);
17     copy(coll.begin(),end,ostream_iterator<int>(cout," "));
18     cout<<endl;
19     cout<<distance(end,coll.end())<<endl;
20     coll.erase(end,coll.end());
21     copy(coll.begin(),coll.end(),ostream_iterator<int>(cout," "));
22     cout<<endl;
23     return 0;
24 }
View Code

   - 辅助函数 distance(), 返回迭代器之间的距离, 可以用于随机存取迭代器(还可以使用算术运算)和双向迭代器.

  -  coll.erase(end,coll.end()); 删除区间内的所有元素. 

  - 更易型算法不适用于关联式容器. 关联式的所有迭代器均被声明为指向常量. 更易关联式容器中的元素会产生编译错误.

  - 不能使用算法函数 remove() 来移除关联式容器的元素, 但是可以通过调用关联式容器的方法erase()移除元素,返回被删除元素个数.

11. 算法函数和成员函数

  - 容器本身可能提供功能相似算法函数而性能更佳的成员函数, 成员函数更了解具体容器的具体需要, 因此应该优先考虑成员函数.

  - 有的容器操作, 使用成员函数和算法函数都可以. 但是成员函数性能更高.

12. 自定义泛型函数

13. 函数作为算法的参数

  - 算法函数:  for_each(coll.begin(),coll.end(), print) ;// print 是一个函数 

        std::transform(coll1.begin(),coll1.end(),std::back_inserter(coll2),square); //square 是一个函数 

          pos = find_if(coll.begin(),coll.end(), isPrime); 

  - 判断式

    - 判断式通常用来指定排序准则和搜索准则, 不允许改变参数的状态. 如 isPrime

    - 如果元素本身不支持 operator < ,那么一般会使用二元判断式(2个参数).

14. 仿函数(function object: 函数对象)

  - 定义:  可以这么理解, 任何东西只要能像函数一样调用 , 就说它是个函数, 因此一般的对象如果重载了operator (), 那么就能像函数一样调用, 成为一个函数对象. 

  - 例子:

 1 #include <iostream>
 2 #include <map>
 3 #include <string>
 4 #include <iterator>
 5 #include <list>
 6 #include <algorithm>
 7 #include <vector>
 8 using namespace std;
 9 class PrintInt{
10 public:
11     void operator()(int elem) const{
12         cout<<elem<<endl;
13     }
14 };
15 int main(){
16     vector<int> coll;
17     for(int i=1;i<=9;++i){
18         coll.push_back(i);
19     }
20     for_each(coll.begin(),coll.end(),PrintInt());
21     cout<<endl;
22     return 0;
23 }
View Code

  - 函数对象的优点:

    - 是智能型函数:

      - 函数对象有自己的类型, 可以拥有其他成员函数和成员变量, 意味着函数对象可以拥有状态. 

      - 可以在执行期间初始化它们.

    - 每个函数对象有自己的型别:

      - 一般函数只有当signature不同时才算型别不同, 而函数对象即使签名相同, 也可以有不同的类型.

      - 可以设计仿函数继承体系, 完成其他事情.

    - 仿函数比一办函数快

      - 对template而言 , 更多的细节在编译期就已确定, 所以运行更佳.

      - 例子

 1 #include <iostream>
 2 #include <iterator>
 3 #include <list>
 4 #include <algorithm>
 5 using namespace std;
 6 //function object that adds the value with witch it is initialized.
 7 class AddValue{
 8 private:
 9     int theValue;  // the value to add
10 public:
11     AddValue(int v):theValue(v){}
12     void operator()(int& elem) const{
13         elem += theValue;
14     }
15 };
16 
17 int main(){
18     list<int> coll;
19     for(int i=1;i<=9;++i){
20         coll.push_back(i);
21     }
22     copy(coll.begin(),coll.end(),ostream_iterator<int>(cout," "));
23     cout<<endl;
24     for_each(coll.begin(),coll.end(),AddValue(10));
25     copy(coll.begin(),coll.end(),ostream_iterator<int>(cout," "));
26     cout<<endl;
27     for_each(coll.begin(),coll.end(),AddValue(*coll.begin()));
28     copy(coll.begin(),coll.end(),ostream_iterator<int>(cout," "));
29     cout<<endl;
30     return 0;
31 }
View Code

  - STL中预定义的仿函数

    -  less<int>() //默认的排序准则 

    -  negate<int>(); // 处理数值, 转换为相反值.   transform(coll.begin(),coll.end(),coll.begin(), negate<int>()); // 求相反数并回写. 

    -  multiplies<int>(); // 处理数值, 相乘   transform(coll.begin(),coll.end(),coll.begin(),coll.begin(),multiplies<int>()); // 3个集群相同, 求的是平方, 保存回去. 

    -  mem_fun_ref(&Person::save) //调用它所作用的元素的某个成员函数,参数可以是Person的派生类.    for_each(coll.begin(), coll.end(), mem_fun_ref(&Person::save));  

    - 仿函数和适配器结合使用, 更加灵活; 而且这样这些仿函数都是inline, 性能更佳.

 1 #include <iostream>
 2 #include <iterator>
 3 #include <set>
 4 #include <queue>
 5 #include <algorithm>
 6 using namespace std;
 7 
 8 int main(){
 9     // 可以在创建set的时候指定set内的排序规则.(?)
10     set<int,greater<int> > coll1;  // 为什么不是 greater<int>()呢, greater不是一个类吗? 
11     deque<int> coll2;
12     for(int i=1;i<=9;++i)
13         coll1.insert(i);
14     copy(coll1.begin(),coll1.end(),ostream_iterator<int>(cout," "));
15     cout<<endl;
16     //使用适配器 bind2nd() 将预先定义的仿函数和其他数值组合在一起.
17     // bind2nd会将10当成内部数值保存下来,当算法以实际集群元素为参数, 
18     //调用bind2nd时, bind2nd会把该元素当成第一参数, 10当成第二参数,调用bind2nd的第一参数(仿函数).
19     transform(coll1.begin(),coll1.end(),back_inserter(coll2),bind2nd(multiplies<int>(),10));//这里是multiplies<int>(),有括号呀?
20     copy(coll2.begin(),coll2.end(),ostream_iterator<int>(cout," "));
21     cout<<endl;
22     replace_if(coll2.begin(),coll2.end(),bind2nd(equal_to<int>(),70),42);
23     copy(coll2.begin(),coll2.end(),ostream_iterator<int>(cout," "));
24     cout<<endl;
25     coll2.erase(remove_if(coll2.begin(),coll2.end(), // range
26         bind2nd(less<int>(),50)), // 删除标准
27         coll2.end());
28     copy(coll2.begin(),coll2.end(),ostream_iterator<int>(cout," "));
29     cout<<endl;
30     return 0;
31 }
View Code
原文地址:https://www.cnblogs.com/roger9567/p/4888770.html