侯捷 STL

泄露秘密 提交于 2020-01-22 16:42:24

侯捷 STL

源码之前

了无秘密

1.1 headers、版本、重要资源

  • C++ Standard Library(标准库) vs. Standard Template Library(标准模板库)

2.2 STL 体系结构基础

  • 六大部件:

    1. 容器(Container)
    2. 分配器(Allocators)
    3. 算法(Algorithms)
    4. 迭代器(Iterators)
    5. 适配器(Adapters)
    6. 仿函数(Functors)
    #include<vector>
    #include<algorithm>
    #include<functional>
    #include<iostream>
    using namespace std;
    
    int main()
    {
        int ia[6] = {27, 210, 12, 47, 109, 83};
        vector< int , allocator<int> > vi(ia,ia + 6);
        cout << count_if(vi.begin(), vi.end(),
                        not1(bind2nd( less<int>(), 40 )));
        return 0;
    }
    
  • 复杂度,Complexity,Big-Oh

  • “前闭后开”区间

  • range-base for statement and auto (C++11)

3.3 容器 - 结构与分类(一)

  • 大致上分为两种,一个叫做序列式(Sequence Containers),一种叫做关联式(Associative Containers),还有种一种 C++ 11的 Unordered Containers

4.4 容器 - 结构与分类(二)

  • vector

5.5 & 6.6容 器 - 结构与分类 (三 & 四)

  • list / push_back / list.sort() /

  • list.sort()这样调用 sort 指的是调用 list 里面的 sort 排序而不是全局里面的排序,使用容器里面的 sort 一定比使用全局的 sort 要快

  • forward_list 单向链表(C++ 11)

  • slist 也是单向链表(GNU很早以前就有)

  • deque 双向开口两边都可进可出(了解 buffer 的概念,扩充的时候每次扩充一个buffer),deque 没有自己的 sort 和 find,使用功能得用 global 的

  • stack ,借用deque,容器的 adapter,常用的接口是 push ,和 pop

  • queue,借用deque,容器的 adapter,常用的接口是 push ,和 pop

  • 由于 stack 和 queue 这两种容器没有自己的数据结构,它们是借用 deque 所以有人从技术上不把这两个容器叫作容器,叫作容器的 adapters


  • 关联式,容器 multiset,insert,关联式容器的优点,红黑树做底部

  • multimap,key 和 value,红黑树做底部

  • unordered_multiset 哈希表做底部,放东西用 insert,入如果元素的个数大于篮子(bucket)的时候,篮子要扩充,所以篮子一定要比元素多,载重因子(load_factor)

  • unordered_multimap 同理


  • set 没有重复的元素

  • map key没有重复,value有重复

  • unordered_set

  • unordered_map


  • 过去含有:

  • hash_set

  • hash_map

  • hash_multiset

  • hash_multimap


7.7 分配器(allocator)之测试

  • 作者介绍的是使用 GNU C 的不同分配器,并对不同的分配器在容器中进行测试程式
  • 可以脱离容器单用它的分配器来拿内存和还内存,不过这样确实没有必要,只是测试这个类可以用给你瞅瞅
  • 一般我们在拿内存的时候使用 new 搭配 delete 或者 malloc 搭配 free,当然啊,这些最终归结起来都到了 malloc 搭配 free上去了

8.8 源代码之分布

9.9 OOP(Object-Oriented Programming) VS. GP(Generic Programming)

  • OOP 企图将 datas 和 methods 关联在一起
  • GP 是把 datas 和 methods 分开来
  • list 不能像 vector 和 deque 一样用全局的 sort 来排序

10.10 技术基础:操作符重载 and 模板(泛化, 全特化, 偏特化)

  • 泛化与(全)特化
  • 偏特化(有两种,一种是个数的偏特化,一种是范围上的偏特化)

11.11 分配器(allocators)

  • 扮演一个幕后英雄的角色,首先我们谈一谈 operator new () 和 malloc()

  • 所有的 C++ 平台调用 operator new都含有 malloc

  • 在VC6的allocator 只是以 ::operator new 和 ::operator delete 完成 allocate() 和 deallocate(),没有任何特殊设计

    //自己调用的话(不建议)通常在容器完成
    int* p = allocator<int>().allocate(512,(int*)0);
    allocator<int>().deallocate(p,512);
    

  • SGI 使用的是 alloc 而不是 allocator(因为它底层使用的 malloc 包装太多),malloc是给数据不同大小的需要设计的所以里面有cookie记录,但是容器中元素的的大小固定所以不需要

    //用例(后来GNU 的版本改成之前的了,不知道为什么,但是仍然含有alloc,改为(__pool_alloc)
    vector<string,__gnu_cxx::__pool_alloc<string>> vec;
    //__gnu_cxx 在这个命名空间里面,源码里面可得
    

12.12 容器之间的实现关系与分类

  • 各个容器之间的关系图

13.13 深度探索list(上)

  • 可见书中 P129

  • GNU2.9 中的alloc分配器,因为list是非连续的,所以它的iterator不能是说是指针,要够聪明(够智能),所有的迭代器除了vector和array之外,他都要是一个类,这样他才能是一个智能指针含有typedef __list_iterator<T,T&,T*> iterator;

  • C++不允许i++++(即(i++)++)

14.14 深度探索list(下)

  • 环状双向list,在最后一个元素后面还有一个end空白结点,便能够符合对于“前闭后开”区间的要求
  • G2.9版是4大小,G4.9是8的原因

15.15*(重要)* 迭代器的设计原则和 Iterator Traits的作用与设计

  • Iterator 必须提供的5种 associated types(相关型别)

  • 指针也是一种特化的Iterator,而Iterator 是一种泛化的指针,设计 traits 来区分是以class来表现的 Iterator 还是以指针的Iterator——相当于加上一个中介层来解决这个问题

  • typedef typename __type_traits<T>::has_trivial_destructor trivial_destructor;
    

    它是将__type_traits<T>这个模板类中的has_trivial_destructor嵌套类型定义一个叫做trivial_destructor的别名,清晰明了——typename的使用(很牛批)

16.16 vector 深度探索

  • vector 的自动扩充,大家都默认是两倍成长
  • 为什么insert_aux 函数还要多进行一次检查操作?因为把这个函数封装起来可能还有其他的容器可以调用

17.17 array 、forward_list 深度探索

18.18 deque 深度探索(上)

  • deque 的 iterator(指出 node、first、last、current)
  • deque是一个分段连续的,迭代器维持连续的假象,走到边界时有能力调到下一个buffer(缓冲区),至于cur 的作用是表示
  • 几乎所有的容器都提供begin和end

19.19 deque 、queue、stack深度探索(下)

  • queue、stack都可以选择底层结构是deque和list

  • stack和queue不允许你随意放元素,所以都不允许遍历,不给你迭代器

  • stack和queue都不可以选择set 或map作为底层结构

20.20 RB-tree

  • 关联式的容器查找、元素的安插都非常快
  • 红黑树是平衡二元搜索树,提供遍历的功能,不应该使用红黑树的iterator来改变元素值

21.21 容器 set、multiset 深度探索

  • 两者的底层结构都是 rb_tree,因此有元素自动排序的特点,排序的依据是 key,而set / multiset元素的value 和key 合一:value 就是 key

22.22 容器 map、multimap 深度探索

  • map的“[]”的重载

23.23 & 24.24 容器 hashtable 深度探索

  • 他比红黑树简单多,红黑树太复杂了,这个我可以讲的很精彩,hashtable 哈希表又叫 散列表
  • hashtable 采用 Separate Chaining,链表越长,搜寻就越慢,bucket需要打散增加为两倍(质数),一般当元素个数超过bucket的个数,就得 rehashing,一般bucket为质数
  • hash function(不唯一,够乱就好) 传出来的东西就是 hash code
  • 标准库里面没有针对C++ 的字符串的hash function 有需要就得自己写一个
  • 有了hash code之后,modulus运算(就是除了以后得到余数的运算)

25.25 hash_set、hash_multiset, hash_map,hash_multimap

  • 这与之前所讲的区别是他们是以 hash table 为支撑的

26.26 unordered 容器概念

27.27 算法的形式

从语言层面上来说:

  • 容器 container 是个 class template
  • 算法 Algorithm 是个 function template
  • 迭代器 Iterator 是个 class template
  • 仿函数Functor 是个 class template
  • 适配器Adapter 是个 class template
  • 分配器 Allocator 是个 class template

28.28 迭代器的分类(P92)

29.29 iterator_category 对算法的影响(P99)

  • distance 和advance 的例子

  • Iterator_category 和 type trait 对算法的影响

  • 泛化(generalization)特化(specialization)强化(refinement)

  • Iterator traits 与 Type Traits

  • 算法源码中对iterator_category 的“暗示”

30.30 算法源代码剖析(11个例子)

34.34 not1

cout << count_if(vi.begin(), vi.end(),
                    not1(bind2nd( less<int>(), 40 )));

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-pA6Uc2SA-1579674514530)(https://i.postimg.cc/CK2FvYv1/capture-20200114211635954.png)]

35.35 bind

  • Cplusplus网站

  • std::bind 可以绑定

    1. functions
    2. function objects
    3. member functions
    4. data members
    // bind example
    #include <iostream>     // std::cout
    #include <functional>   // std::bind
    
    // a function: (also works with function object: std::divides<double> my_divide;)
    double my_divide (double x, double y) {return x/y;}
    
    struct MyPair {
      double a,b;
      double multiply() {return a*b;}
    };
    
    int main () {
      using namespace std::placeholders;    // adds visibility of _1, _2, _3,...
    
      // binding functions:
      auto fn_five = std::bind (my_divide,10,2);               // returns 10/2
      std::cout << fn_five() << '\n';                          // 5
    
      auto fn_half = std::bind (my_divide,_1,2);               // returns x/2
      std::cout << fn_half(10) << '\n';                        // 5
    
      auto fn_invert = std::bind (my_divide,_2,_1);            // returns y/x
      std::cout << fn_invert(10,2) << '\n';                    // 0.2
    
      auto fn_rounding = std::bind<int> (my_divide,_1,_2);     // returns int(x/y)
      std::cout << fn_rounding(10,3) << '\n';                  // 3
    
      MyPair ten_two {10,2};
    
      // binding members:member fuction 其实有一个argument:this
      auto bound_member_fn = std::bind (&MyPair::multiply,_1); // returns x.multiply()
      std::cout << bound_member_fn(ten_two) << '\n';           // 20
    
      auto bound_member_data = std::bind (&MyPair::a,ten_two); // returns ten_two.a
      std::cout << bound_member_data() << '\n';                // 10
    
      return 0;
    }
    
    vector<int> v{15,37,94,50,28,73,58,98};
    int n = count_if(v.cbegin(),v.cend(),not1(bind2nd(less<int>(),50)));
    cout << "n= " << n <<endl;
    
    auto fn_ = bind(less<int>(),_1,50);
    cout << count_if(v.cbegin(),v.cend(),fn_) << endl;
    cout << count_if(v.cbegin(),v.cend(),bind(less<int>(),_1,50)) << endl;
    

36.36 reverse_iterator(迭代器适配器)

  • //迭代器适配器
    reverse_iterator
    rbegin()
    {return reverse_iterator(end());}
    
    reverse_iterator
    rend()
    {return reverse_iterator(begin());}
    
  • 里面有一个正向的迭代器,但实现的是反操作

37.37 insert

  • copy 的应用过程,insert 不用担心 copy 的不足

    list<int> foo,bar;
    for(int i=1;i<5;i++)
    {foo.push_back(i);bar.push_back(i*10);}
    
    list<int>::iterator it = foo.begin();
    advance(it,3);//不可以直接加,他是链表,不是顺序存储的
    
    copy(bar.begin(),bar.end(),insert(foo,it));
    

38.38 ostream_iterator

  • X 适配器:ostream_iterator,可以把迭代器绑定到一个装置上

39.39 istream_iterator

//istream_iterator example
#include<iostream>
#include<iterator>

int main()
{
double value1,value2;
std::cout << "please,insert two values:";
std::istream_iterator<double> iit(std::cin);
if(iit!=eos)vale2 = *iit;

++iit;
if(iit!=eos)value2=*iit;

std::cout << value1 << "*" <<value2<<"=" <<(value1*value2)<<"\n";
return 0;
}

40.40 一个万用的 hash function

41.41 Tuple 用例

42.42 & 43.43 Type traits

44.44 cout

  • 是一个对象

45.45 & 46.46 movable元素对于deque速度效能的影响与测试代码

se,insert two values:";
std::istream_iterator iit(std::cin);
if(iit!=eos)vale2 = *iit;

++iit;
if(iit!=eos)value2=*iit;

std::cout << value1 << “" <<value2<<"=" <<(value1value2)<<”\n";
return 0;
}


## 40.40 一个万用的 hash function

## 41.41 Tuple 用例

## 42.42 & 43.43 Type traits

## 44.44 cout

- 是一个对象

## 45.45 & 46.46 movable元素对于deque速度效能的影响与测试代码

- 浅拷贝、深拷贝、move

- 写的有点粗糙,大家见谅,材料一枚,建议大家多多使用!
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!