c++STL

容器

课程中使用了 gcc 2.9,旧版本中继承关系更少,代码更简洁。更容易阅读,且原理相同

  1. 容器 Containers

    序列容器 Sequence Containers

    Array
    Vector 尾端可扩充的数组
    Deque 双端可扩充的数组
    List 双向队列
    Forward-List 单向队列

    关联容器 Associative Containers 通过红黑树实现的有序容器

    Set key不可重复
    Multiset key可重复
    Map
    Multimap

    无序容器 Unordered Containers 哈希表实现的容器,使用拉链法

    Unordered Set Multiset
    Unordered Map Multimap

  2. 分配器 Allocators

  3. 算法 Algorithms

  4. 迭代器 Iterators

  5. 适配器 Adapters

  6. 仿函数 Functors

int ia[6] = {1,2,3,4,5,6};
vector<int, allocator<int>> vi(ia, ia+6); // vector的第二个参数是空间分配器
count << count_if(vi.begin(), vi.end(), 
                  not1(bind2nd(less<int>(), 40)));//统计大于等于40的个数

STL中使用“左闭后开”表示数据范围。c.begin()表示第一个元素,c.end()表示最后一个元素后面的位置。

Container<T>::iterator c;
for (auto c = vec.begin; c != vec.end(); ++c);
for (auto& c : vec);

分配器

每个容器有个默认分配器,可以在尖括号内指定。分配器底层还是调用了malloc

// vector 的定义,可见模板参数中有指定内存分配器的参数,且此参数有默认值
template <typename _Tp, typename _Alloc=std::allocator<_Tp>>
  class vector : protected _Vector_base<_Tp, _Alloc>

gcc 下的拓展分配器,不同编译器下不同。

#ifdef __GNUC__		
#include <ext\array_allocator.h>
#include <ext\mt_allocator.h>
#include <ext\debug_allocator.h>
#include <ext\pool_allocator.h>
#include <ext\bitmap_allocator.h>
#include <ext\malloc_allocator.h>
#include <ext\new_allocator.h>  
#endif


void fun() {
    // 其定义在非std命名空间
    list<string, __gnu_cxx::malloc_allocator<string>> c2;  	//3110
    list<string, __gnu_cxx::new_allocator<string>> c3; 		//3156
    list<string, __gnu_cxx::__pool_alloc<string>> c4;  		//4922
    list<string, __gnu_cxx::__mt_alloc<string>> c5; 		//3297
    list<string, __gnu_cxx::bitmap_allocator<string>> c6;  	//4781 	
    
    // 分配器也是一个类,可以直接使用。其只有allocate和deallocate两个方法。一般没必要使用,而是使用new delete malloc free
	int* p; 	
    allocator<int> alloc1;	
	p = alloc1.allocate(1);  
	alloc1.deallocate(p,1); 	
						
	__gnu_cxx::malloc_allocator<int> alloc2;  
	p = alloc2.allocate(1);  
	alloc2.deallocate(p,1);  	
		
    __gnu_cxx::new_allocator<int> alloc3; 	
	p = alloc3.allocate(1);  
	alloc3.deallocate(p,1); 	
		
	__gnu_cxx::__pool_alloc<int> alloc4;  	
	p = alloc4.allocate(2);  // 分配两个int大小的空间
	alloc4.deallocate(p,2);  // 释放两个int大小的空间,释放空间时与free delete不同,需要指定大小
		
	__gnu_cxx::__mt_alloc<int> alloc5; 	
	p = alloc5.allocate(1);  
	alloc5.deallocate(p,1);  	
			
    __gnu_cxx::bitmap_allocator<int> alloc6;  	
	p = alloc6.allocate(3);  
	alloc6.deallocate(p,3);
}

OOP 面向对象编程 与 GP 泛型编程

OOP将data与methods包装在一起,GP则是将二者分开。

OOP
所有工作都由类开发者完成

class Class<typename T> {
    ...
    T min(const T& a, const T& b) {
        return a < b ? a : b;
    }
}

GP
容器团队与算法团队可以分工合作

class Class<typename T> {}

template<typename _Tp> 
const _Tp& min(const _Tp& __a, const _Tp& __b) {}

list 的 sort 方法

一般容器在排序时都使用全局排序方法 ::sort() ,而双向队列 list 的排序方法在自己内部定义。

全局排序方法中需要对指针或泛化指针进行加减乘除等操作,这就要求是随机指针,而list是双向链表。

以下都是 gcc2.9 版本的源代码

lists

在尾端多添加了一个空白节点,以实现“左闭右开”区间。底层是一个循环双向链表。

template<typename _Tp, typename _Alloc = alloc >
class list {
protected:
    typedef __list_node<T> list_node; // node节点
public:
    typedef list_node* link_type;
    typedef __list_iterator<T, T&, T*> iterator; // 迭代器本质上是一个指针
protected:
    link_type node; // 指向节点的指针,2.9版本占4字节
                    // 4.9版本占8字节,因为其父类拥有一个链表节点__List_node_base,其包含了两个指针
// ...
};

template <class T>
struct __list_node {
    typedef void* void_pointer;
    void_pointer prev; // 指向前后的指针,没增加一个元素都会多占用的空间
    void_pointer next; // 在4.9中不再使用void*
    T data;
};

template<class T, class Ref, class Ptr> //   新版中只传入一个参数 T
struct __list_iterator { // 类似指针的类
    typedef __list_iterator<T, Ref, Ptr> self;
    typedef __list_node<T>* link_type;
    
    /*每个容器的 iterator 都一定要有以下5个 typedef*/
    typedef bidirectional_iterator_tag iterator_category; // 迭代器的种类:只能向前/可以后退/每次只能加1/每次可以增加大于1的值
    typedef T   value_type;
    typedef Ptr pointer;
    typedef Ref reference;
    typedef ptrdiff_t difference_type; // 表示两个元素的距离的变量类型 unsigned int/
    
    link_type node;
// ...
    reference operator*() const { 
        return (*node).data;
    }
    pointer operator->() const { 
        return &(operator*()); 
    }
    self& operator++() { // ++i
        node = (link_type) ((*node).next);
        return *this;
    }
    self operator++(int) { // i++
        // 此处调用copy ctor用于创建tmp,并以*this为初值,不会唤起operator*操作符
        // 因为*this已被解释为ctor的参数
        self tmp = *this;
        ++*this; // 使用到了已重载的 operator++()
        return tmp;
    }
    __list_iterator(const iterator& x) : node(x.node) {}
// ...
};

vector

  |+++++++++++++++++++++++++++|----------------|
start                         finish    end_of_storage
  <-----------size------------>
  <------------------capacity------------------>
当空间使用完时申请一个2倍大小的空间,然后将旧数据复制过去  
template <class T, class Alloc = alloc>
class vector {
public:
    typedef T           value_type;
    typedef value_type* iterator_pointer; // 直接将 native pointer 当作迭代器
    typedef value_type& iterator_reference;
    typedef size_t      size_type;
protected:
    // 容器vector的大小就是这三个指针的大小,为12或24
    iterator start;
    iterator finish;
    iterator end_of_storage;
public:
    iterator begin() { return start; }
    iterator end() {return finish; }
    size_type size() const {
        return size_type(end() - begin());
    }
    size_type capacity() const {
        return size_type(end_of_storage - begin());
    }
    bool empty() const { return begin() == end(); }
    reference operator[](size_type n) { return *(begin() + n); }
    reference front() { return *begin(); }
    reference back() { return *(end() - 1); }

   
    void push_back(const T& x) {
        if (finish != end_of_storage) {
            // 当空间充足时向 finish 位置加入元素
            construct(finish, x);
            ++finish;
        }
        else
            // 空间不足时申请双倍空间在添入元素
            insert_aux(end(), x);
    }
    
    template <class T, class Alloc>
    void vector<T, Alloc>::insert_aux(iterator position, const T& x) {
        if (finish != end_of_storage) {
            // 与vector中操作类似,因为这个方法不仅仅被某一个容器调用
            construct(finish, x);
            ++finish;
            T x_copy = x;
            copy_backward(position, finish - 2, finish - 1);
            *position = x_copy;
        }
        else {
            const size_type old_size = size();
            const size_type len = old_size != 0 ? 2 * old_size : 1;
            
            iterator new_start = data_allocator::allocate(len);
            iterator new_finish = new_start;
            // 复制元素,期间涉及每个元素的拷贝构造函数与析构函数,空间的销毁
            try {
                
            } catch () {
                
            }
            destory(begin(), end());
            deallocate();
            start = new_start;
            finish = new_finish;
            end_of_storage = new_start + len;
        }
    }
    
      void
      push_back(const value_type& __x)
      {
	if (this->_M_impl._M_finish != this->_M_impl._M_end_of_storage)
	  {
	    _GLIBCXX_ASAN_ANNOTATE_GROW(1);
	    _Alloc_traits::construct(this->_M_impl, this->_M_impl._M_finish, __x);
	    ++this->_M_impl._M_finish;
	    _GLIBCXX_ASAN_ANNOTATE_GREW(1);
	  }
	else
	  _M_realloc_insert(end(), __x);
      }
};


 

array

将一个数组包装成类,然后它就遵循一定的规则,需要提供迭代器、特殊成员变量等,也可以适应算法库。

template <typename _Tp, std::size_t _Nm>
struct array {
    typedef _Tp          value_type;
    typedef _Tp*         pointer;
    typedef value_type*  iterator;
    
    value_type _M_instance[_Nm ? _Nm : 1];//没有 ctor dtor
    
    iterator begin() { return iterator(&_M_instance[0]); }
    
    iterator end() { return iterator(&_M_instance[_Nm]); }
};

forward_list

线性单项链表

deque

使用了分段存储的思想,一个大数组元素是各个子数组,也称buffer。具体数据存储在buffer中。
每次扩充时新建一个buffer并插入到父数组中,当父数组空间不足时父数组也扩容。
子数组大小不变,扩容阔的是父数组,父数组为vector。
每次扩容时将旧数据复制到数组的中段,为的是左右端都有相同空间。
   |------|------|------|------|      |      | => 增长方向
   |      |      |      |1|2|3|4|5|6|7|8|9| |
   |      |      |1|2|3|4|5|6|7|8|9|10|   |
   |      |1|2|3|4|5|6|7|8|9|10|         finish[end()返回的尾指针]指向此处
   | | | |4|5|6|7|8|9|10|
         |
       start[begin()返回的头指针]指向此处
iterator包含4个元素
    cur   指向当前元素
    first 指向当前buffer中第一个元素
    last  指向当前buffer中最后一个元素
    node  指向父数组中的当前节点
template <class T, class Alloc = alloc, size_t BufSiz = 0>//旧版本可以指定buffer大小,新版不可以
class deque {
public:
    typedef T value_type;
    typedef __deque_iterator<T> iterator;
protected:
    typedef prointer* map_pointer; // T**

    iterator start;
    iterator finish;
    map_pointer map;
    size_type map_size;
public:
    iterator begin() {return start;}
    iterator end() {return finish;}
    size_type size const {return finish - start;}
    
    iterator insert(iterator position, const value_type& x) {
        if (position.cur == start.cur) {//插到最前端交给 push_front 做
            push_front(x);
            return start;
        } else if (position.cur == finish.cur) {//插到最后段交给 push_back 做
            push_back(x);
            iterator tmp = finish;
            --tmp;
            return tmp;
        } else {
            return insert_aux(position, x);
        }
    }
    
    template <class T, class Alloc, size_t BufSize>
    typename deque<T, Alloc, BufSize>::iterator
    deque<T, Alloc, BufSize>::insert_aux(iterator pos, const value_type& x) {
        differenct_type index = pos - start; // 安插点之前的元素个数
        value_type x_copy = x;
        if (index < size() / 2) { // 如果安插点之前的元素较少
            push_front(front());  // 在最前端加入与第一个元素相同的元素
            // ...
            copy(front2, pos1, font1); // 元素搬移
        } else {                       // 如果安插点之后的元素较少
            push_back(back());         // 在最后端加入与最后一个元素相同的元素
            // ...
            copy_backward(pos, back2, back1); // 元素搬移
        }
        *pos = x_copy; // 安插点设置新值
        return pos;
    }
/******************   如何模拟连续空间? 依靠迭代器,对操作符进行了重载  **********************/
    reference operator[](size_type n) {
        return start[difference_type(n)];
    }
    reference front() { return *start; }
    reference back() {
        iterator tmp = finish;
        --tmp;
        return *tmp;
    }
    size_type size() const { return finish - start; }
    bool empty() const { return finish == start; }
// ...
};

template <class T>
struct __deque_iterator {
    typedef random_access_iterator_tag iterator_category; 
    typedef T   value_type;
    typedef T*  pointer;
    typedef T&  reference;
    typedef ptrdiff_t difference_type;
    
    typedef size_t size_type;
    typedef T** map_potinter;
    typedef __deque_iterator self;
    
    // 迭代器大小为 16 或 32
    T* cur;
    T* first;
    T* last;
    map_pointer node;
    
    reference operator*() const {
        return *cur;
    }
    pointer operator->() const {
        return &(operator*());
    }
    difference_type
    operator-(const self& x) const {
        return difference_type(buffer_size()) * (node - x.node - 1) +//buffer_size函数用于获取buffer大小
            (cur - first) + (x.last - x.cur);
        // 当前buffer的元素量    被减数buffer元素量
    }
    self& operator++() {
        ++cur;
        if (cur == last) {
            set_node(node + 1);
            cur = first;
        }
        return *this;
    }
    self operator++() {
        self tmp = *this;
        ++*this;
        return tmp;
    }
    // 切换到父数组中下一个元素
    void set_node(map_pointer new_node) {
        node = new_node;
        first = *new_node;
        last = first + difference_type(buffer_size());
    }
    self& operator--() {
        if (cur == first) {
            set_node(node - 1);
            cur = last;
        }
        --cur;
        return *this;
    }
    self operator--(int) {
        self tmp = *this;
        --*this;
        return tmp;
    }
    self& operator+=(difference_type n) {
        difference_type offset = n + (cur - first);
        if (offset >= 0 && offset < difference_type(buffer_size())) {
            cur += n;
        } else {
            difference_type node_offset = 
                offset > 0 ? 
                offset / difference_type(buffer_size()) 
                : -differnece_type((-offset - 1) / buffer_size() - 1);
            set_node(node + node_offset);
            cur = first + (offset - node_offset * difference_type(buffer_size()));
        }
        return *this;
    }
    self operator+(difference_type n) const {
        self tmp = *this;
        return tmp += n;
    }
// ...
};

queue stack

内部包含一个deque,用自己的队列 / 栈方法封装对deque的操作。
都不允许遍历,也不提供 iterator。
都可以选择使用list作为底层结构。
queue不可选择vector当作底层结构,stack可选择vector当底层结构。

Iterator 的设计原则

迭代器作为算法与容器间的桥梁。

template<typename _Tp>
struct _List_iterator {
    /**
     * 每个容器的 iterator 都一定要有以下5个 typedef
     */
    // 迭代器的种类:只能向前/可以后退/每次只能加1/每次可以增加大于1的值
    // bidirectional_iterator_tag 代表可以双向迭代
    typedef bidirectional_iterator_tag iterator_category; 
    typedef T   value_type;
    typedef T*  pointer;
    typedef T&  reference;
    typedef ptrdiff_t difference_type; // 表示两个元素的距离的变量类型 unsigned int 等
};

template<typename I>
inline void
algorithm(I first, I last) {
    // 在算法中会直接使用以上的5中自定义类型
    I::iterator_category;
    I::pointer;
    I::reference;
    I::value_type;
    I::difference_type;
}

iterator traits 迭代器萃取机

/*
算法库不仅可以作用于 class pointer(泛化的指针) 还可以作用于 native pointer(退化的迭代器)
使用 iterator traits 进行分辨:这个traits必须有能力分辨 iterator 是 class iterator T 还是 native pointer to T
利用 partial specialization 可以达到目标
*/
template <class T>
struct iterator_traits { // I 为 class iterator
    typedef typename I::iterator_category iterator_category;
    typedef typename I::value_type        value_type;
    typedef typename I::difference_type   difference_type;
    typedef typename I::pointer           pointer;
    typedef typename I::reference         reference;
};
// 利用 partial specialization 
template <class T>
struct iterator_traits<T*> { // I 为 pointer to T
    typedef random_access_iterator_tag iterator_category;
    typedef T                          value_type;
    typedef ptrdiff_t                  difference_type;
    typedef T*                         pointer;
    typedef T&                         reference;
};
template <class T>
struct iterator_traits<const T*> { // I 为 pointer to const T
    typedef random_access_iterator_tag iterator_category;
    typedef T                          value_type;// 注意不是 const T,这里加上const后无法初始化赋值
    typedef ptrdiff_t                  difference_type;
    typedef T*                         pointer;
    typedef T&                         reference;
};

template<typename I, ...>
void algorithm(...) {
    // 算法获取I的value type时可使用这种方法
    typename iterator_traits<I>::value_type v1;
}

RB_tree 红黑树

C++中rb-tree 可以使用迭代器改变元素的值,但不应用其改变key的值,使用场景是在map结构中改变value的值。rb-tree提供两种insert操作:1. insert_unique() key不可以重复 2. insert_equal() key可以重复

begin() 返回指向最左下角的元素的迭代器,end() 返回指向最右下角的元素的迭代器。在root节点之前有一个header指针指向的一个哑节点,这个节点还指向 begin() end()

template <class Key, 
          class Value, // 键值对合并后的数据
          class KeyOfValue,  // 如何从键值对中拿到key
          class Compare, 
          class Alloc=alloc>
class rb_tree {
protected:
    typedef __rb_tree_node<Value> rb_tree_node;
    ...
public:
    typedef rb_tree_node* link_type;
    ...
protected:
    size_type node_count; // rb_tree的大小 节点个数
    link_type header;
    Compare key_compare; // 模板参数,一个 function object
    ...
};

// 使用方法
rb_tree<int, 
        int, // 意味着只有键没有值
        identity<int>,  // 这个identity是gcc独有的
        less<int>, 
        alloc> myTree;

template<class T>
struct identity : public unary_function<T, T> {
    const T& operator() (const T& x) const {
        return x;
    }
};

template<class T>
struct less : public binary_function<T, T, bool> {
    bool operator() (const T& x, const T& y) const {
        return x < y;
    }
};

_Rb_tree对象关系

set multiset

无法使用迭代器改变元素值,set multiset的迭代器底部就是RBtree的 const iterator,就是禁止对元素赋值。
set使用 insert_uniqui(); multiset 使用 inert_equal()

set map 都是一种 container adapter。

template <class Key,
          class Compare = less<Key>,
          class Alloc = alloc>
class set {
public:
    typedef Key key_type;
    typedef Key value_type;
    typedef Compare key_compare;
    typedef Compare value_compare;
private:
    typedef rb_tree<key_type, value_type, 
            identity<value_type>, key_compare, Alloc> rep_type;
    rep_type t; // 具体工作都调用rbtree实现
public:
    typedef typename rep_type::const_iterator iterator; // 此处迭代器不允许改变
    // ...
};

// 使用方法
set<int> iset;
// 等于
set<int, less<int>, alloc> iset;
// 底层使用
template<int, int, identity<int>, less<int>, alloc>
class rb_tree;

map multimap

与set类似,value包括key加data。

template <class Key, class T, 
          class Compare = less<Key>, class Alloc=alloc>
class map {
public:
    typedef Key key_type;
    typedef T data_type;
    typedef T mapped_type;
    typedef pair<const Key, T> value_type;// const Key 保证了key不可以改变
    typedef Compare key_compare;
private:
    typedef rb_tree<key_type, value_type, 
                    select1st<value_type>, // select1st与identify类似都是gcc独有的
                    key_compare, Alloc> rep_type;
    rep_type t;
public:
    typedef typename rep_type::iterator iterator;//map的迭代器就是红黑树的迭代器
    
    // 通过key取得data,如果key不存在则使用默认值创建键值对
    mapped_type& operator[] (const key_type& k) {
        // 二分查找获取位置,不存在时返回应该处于的位置
        iterator i = lower_bound(k);
        if (i == end() || key_comp()(k, (*i).first))
            i = insert(i, value_type(k, mapped_type()));
        return (*i).second;
    }
// ...
};

// 使用
map<int, string> imap;
map<int, string, less<int>, alloc> imap;
// 底层
template <int, pair<const int, string>, // const Key 保证了key不可以改变
         select1st<value_type>, less<int>, alloc>
         class rb_tree

hashtable

template <class Value, class Key, class HashFcn, // 哈希函数
          class ExtractKey, class EqualKey, class Alloc=alloc>
class hashtable {
public:
    typedef HashFcn hasher;
    typedef EqualKey key_equal;
    typedef size_t size_type;
private://三个函数对象(1*3) + 一个vector(4*3) + 元素总数(4) = 19,对齐调整为20
    hasher hash;
    key_equal equals;
    ExtractKey get_key;
    typedef __hashtable_node<Value> node;
    vector<node*, Alloc> buckets;
    size_type num_elements;
public:
    size_type bucket_count() const {
        return buckets.size();
    }
    
    template <class Value, class Key, class HashFcn, class ExtractKey, 
              class EqualKey, class Alloc=alloc>
    struct __hashtable_iterator {
        // ...
        node* cur;
        hashtable* ht;
    };
    
    template<class Value>
    struct __hashtable_node {
        __hashtable_node* next;
        Value val;
    };
// ...
};

// 使用
hashtable<const char*, const char*, hash<const char*>, // hash模板可能需要用户自己自定义偏特化场景
    identity<const char*>, eqstr, alloc>               // 标准库没有提供 hash<std::string>
    ht(50, hash<const char*>(), eqstr());
ht.insert_unique("kiwi");

// cstring使用strcmp判断是否相等但它返回-1 0 1,而不是bool,需要一层转换。
struct eqstr {
    bool operator()(const char* s1, const char* s2) const {
        return strcmp(s1, s2) == 0;
    }
};


// 拓展出的 set unordered_set map unordered_multimap
template <typename T, 
          typename Hash = hash<T>, 
          typename EqPred = equal_to<T>, 
          typename Allocator = allocator<T>>
class unordered_set;
template <typenaem Key, typename T, 
          typename Hash = hash<T>, 
          typename EqPred = equal_to<T>, 
          typename Allocator = allocator<pair<cosnt Key, T>>>
class unordered_multimap;

写出对象的hash函数

当对用于hashtable的key时必须要有hash函数

template<>
struct hash<string>
    : public __hash_base<size_t, string>
{
	size_t operator()(const string& ...s) const noexcept {
        return std::Hash_impl::hash(__s.data(), __s.length());
    }
};

算法

是一种 function template,不会直接接触到容器,而是通过迭代器简介处理容器。只有迭代器满足算法的调用才能正常编译。

迭代器分类

使用 struct 表示不同的种类,而不是通过int类型。原因:1. 可以通过模板参数与偏特化进行高效的处理

  1. struct input_iterator_tag
  2. struct output_iterator_tag
  3. struct forward_iterator_tag : public input_iterator_tag
    Forward_List 单链表实现的 HashTable
  4. struct bidirectional_iterator_tag : public forward_iterator_tag
    List RB_tree 双链表实现的 HashTable
  5. struct random_access_iterator_tag : public bidirectional_iterator_tag
    Array Vector Deque
template<typename I>
void display(I itr) {
    cout << typeid(itr).name() << endl;
}

int main() {
    display(array<int, 3>::iterator());
    display(vector<int>::iterator());
    display(list<int>::iterator());
    display(forward_list<int>::iterator());
    display(deque<int>::iterator());
    display(set<int>::iterator());
    display(multiset<int>::iterator());
    display(map<int, int>::iterator());
    display(multimap<int, int>::iterator());
    // Pi
    // N9__gnu_cxx17__normal_iteratorIPiSt6vectorIiSaIiEEEE
    // St14_List_iteratorIiE
    // St18_Fwd_list_iteratorIiE
    // St15_Deque_iteratorIiRiPiE
    // St23_Rb_tree_const_iteratorIiE
    // St23_Rb_tree_const_iteratorIiE
    // St17_Rb_tree_iteratorISt4pairIKiiEE
    // St17_Rb_tree_iteratorISt4pairIKiiEE
    exit(EXIT_SUCCESS);    
}

迭代器对算法的影响

判断处迭代器的分类,然后对于不同的迭代器进行不同的操作从而获得更高的效率。

template<class InputIterator>
inline iterator_traits<InputIterator>::difference_type
__distance(InputIterator first, InputIterator last, 
           input_iterator_tag) { // 获得迭代器种类
    iterator_traits<InputIterator>::difference_type n = 0;
    while (first != last) { // 通过循环遍历得到长度
        ++first; ++n;
    }
    return n;
}
    
template<class RandomAccessIterator>
inline iterator_traits<RandomAccessIterator>::difference_type
__distance(RandomAccessIterator first, RandomAccessIterator last, 
           random_access_iterator_tag) { // 获得迭代器种类
    return last - first; // 对于随机存取直接对指针进行相减
}

// 给其他算法库调用的算法,不直接暴露给用户
template <class InputIterator>
inline iterator_traits<InputIterator>::difference_type
distance(InputIterator first, InputIterator last) {
    typedef typename iterator_traits<InputIterator>::iterator_category category;
    return __distance(first, last, category());//不同情况调用不同的函数求差值
}

accumulate

template<class InputIterator, class T>
T accumulate(InputIterator first, InputIterator last, T init) {
    for (; first != last; ++first)
        init += *first;
    return init;
}
template<class InputIterator, class T, class BinaryOperator>
T accumulate(InputIterator first, InputIterator last, T init, BinaryOperator binary_op) {
    for (; first != last; ++first)
        init = binary_op(init, *first);
    return init;
}
// 符合 BinaryOperator 的可调用类
int fun(int x, int y) { return x + y; }
struct func {
    int operator()(int x, int y) { return x + y; }
} obj;
// obj 就可以作为可调用对象传递,称为 仿函数

for_each

template<class InputIterator, class Function>
Function for_each(InputIterator first, InputIterator last, Function f) {
    for (; first != last; ++first)
        f(*first);
    return f;
}

仿函数 functor

注意:写出的functor必须要继承 binary_funcation 才可以正常用于库算法的运行。

  1. 算术类

  2. 逻辑运算类 && || ~

  3. 相对关系类 == < >

    template<class T>
    struct equal_to : public binary_funcation<T, T, bool> {
        bool operator()(const T& x, const T& y) {
            return x == y;
        }
    }
    

identity select1st select2nd 等都是gcc独有的非标准库函数。

Adapters 适配器

多种适配器,改造容器的叫 Container Adapters,改造迭代器的叫 Iterator Adapters,改造仿函数的叫 Functor Adaptors。把内容包装起来,让底层结构提供功能,适配器向外开放统一接口。

  1. 迭代器适配器
    针对 Iterator Adapters

    typedef x1 iterator_category;
    typedef x2  value_type;
    typedef x3  pointer;
    typedef x4  reference;
    typedef x5 difference_type;
    
  2. 仿函数适配器
    针对 Functor Adaptors

    typedef Arg argument_type;
    typedef Result result_type;
    
    typedef Arg1 first_argument_type;
    typedef Arg2 second_argument_type;
    typedef Result result_type;
    
  3. 容器适配器
    stack queue

functors 可适配(adaptable)的条件

STL 规定每个 Adaptable Function 都应当挑选适当的类继承,Adapt 使用辅助函数中定义的类型别名声明变量进行操作:

/*      选择适当的一个继承     */
// 一个操作数的仿函数
template <class Arg, class Result>
struct unary_function {
    typedef Arg argument_type;
    typedef Result result_type;
};
// 两个参数的仿函数
template <class Arg1, class Arg2, class Result>
struct binary_function {
    typedef Arg1 first_argument_type;
    typedef Arg2 second_argument_type;
    typedef Result result_type;
};

FunctorAdapters函数适配器

function adaptor 通过继承 unary_function bunary_function 获得对于参数、返回值类型的重命名。

bind2nd() (被抛弃,使用 bind)

绑定一个函数或函数对象的第二个参数

/*
将类函数函数 less 绑定第二个参数 40
判断 40 是否合法的方式是将其强转为 `typedef typename Operation::second_argument_type arg2_type` 类型
*/
bind2nd(less<int>(), 40);

// 辅助函数,让用户方便使用 binder2nd<Op>,编译器自动推导Op的type
template <class Operation, class T>
inline binder2nd<Opertation> bind2nd(const Operation& op, const T& x) {
    // typename Operation::second_argument_type 要求 Operation 
    // 必须要有 second_argument_type 类型,即继承 bunary_function
    typedef typename Operation::second_argument_type arg2_type;
    //创建一个 binder2nd 对象,这是一个简化对象创建的方法
    return binder2nd<Operation>(op, arg2_type(x));// 将第二个参数转为 arg2_type 类型
}

// 将某个接受两个参数的 Adaptable Bunary function 转换为 Unary Function
template<class Operation>
class binder2nd : public unary_function<
    typename Operation::first_argument_type, 
    typename Operation::result_type> 
{
protected:
    Operation op;
    typename Operation::second_argument_type value;
public:
    // 构造函数
    binder2nd(const Operation& x, // 第一个参数就是两个参数的函数
              const typename Operation::second_argument_type& y) // 第二个参数
        : op(x), value(y){}
    typename Operation::result_type
    operator()(const Operation::first_argument_type& x) const {
        return op(x, value);
    }
};

conut_if

template <class InputIterator, class Predicate>
typename iterator_traits<InputIterator>::difference_type
count_if(InputIterator first, InputIterator last, 
         Predicate pred) {//保持条件判断类函数
    typename iterator_traits<InputIterator>::difference_type n = 0;
    for (; first != last; ++first) {
        if (pred(*first)) // 每次判断时执行类函数
            ++n;
    }
    return n;
}

not1

template<class Predicate>
class unary_negate : public unary_function<typename Predicate::argument_type, bool> {
protected:
    Predicate pred;
public:
    explicit unary_negate(const Predicate& x) : pred(x) {}
    bool operator()(typename Predicate::argument_type& x) const {
        return !pred(x);
    }
};

bind

可以绑定:

  1. functions
  2. function objects
  3. member functions,_1 必须是某个 object 地址
  4. data members,_1 必须是某个 object 地址

返回一个function object ret,调用 ret 相当于调用上述 1,2,3或相当于取出 4

// binding functions
std::divides<double> my_divide;
bind(my_divide, 10, 2); // return 10/2 , 5
bind(my_divide, placeholders::_1, 2); // return x/2
bind(my_divide, placeholders::_1, placeholders::_2); // return x/y
bind<int>(my_divide, placeholders::_2, placeholders::_1); // return int(y/x)

// binding members
struct MyPair {
    double a, b;
    double multiply() {return a * b;}
};
MyPair ten_two {10, 2};
// member function (即类的成员函数MyPair::multiply) 第一个参数是指向对象本身的指针参数 this
auto bound_memfn = bind(&MyPair::multiply, placeholders::_1); // return x.multiply()
bound_memfn(ten_two); // 20

auto bound_memdata = bind(&MyPair::a, ten_two); // return ten_two.a
bound_memdata(); // 10

auto bound_memdata2 = bind(&MyPair::b, placeholders::_1);//使用占位符表示暂时不绑定this指针 return x.b
bound_memdata2(ten_two); // 2

auto fn = bind(less<int>(), _1, 50); // bind2nd(less<int>(), 50)
count_if(vec.cbegin(), vec.end(), fn);

Iterator Adapters 迭代器适配器

reverse_iterator

reverse_iterator
rbegin() {
    return reverse_iterator(end());
}

reverse_iterator
rend() {
    return reverse_iterator(begin());
}

逆向迭代器

template <class Iterator>
class reverse_iterator {
protected:
    Iterator current; // 对应正向迭代器
public:
    // 逆向迭代器的5种 associated types 都和其对应的正向迭代器相同
    typedef typename iterator_traits<Iterator>::iterator_category iterator_category;
    typedef typename iterator_traits<Iterator>::value_type value_type;
    typedef Iterator iterator_type;           正向迭代器
    typedef reverse_iterator<Iterator> self;  逆向迭代器
    
    explicit reverse_iterator(iterator_type x) : current(x) {}
    reverse_iterator(const self& x) : current(x.current) {}
    iterator_type base() const { return current; }
    // 对逆向迭代器取值,就是将对应正向迭代器退一位取值
    reference operator*() const { Iterator tmp = current; return *--tmp; }
    pointer operator->() const { return &(operator*()); }
    self& operator++() { --current; return *this; }
    self& operator--() { ++current; return *this; }
    self operator+(differnece_type n) const { return self(current - n); }
    self operator-(differnece_type n) const { return self(current + n); }
};

inserter

auto it = foo.begin();
advance(it, 3); // 迭代器向前走3步
copy(bar.begin(), bar.end(), inserter(foo, it));//这种写法本质上是不断插入

foo : 1 2 3 4 5
bar : 7 8 9
=>    1 2 3 7 8 9 4 5
    
template<class InputIterator, class OutputIterator>
OutputIterator copy (InputIterator first, InputIterator last, OutputIterator result) {
    while (first != last) {
        *result = *first; // inserter重载了操作符,所以在赋值时不是单纯的拷贝,而是申请空间再拷贝
        ++reslut; first;
    }
}

// 辅助函数,帮用户获取 insert_iterator
template<class Container>
inline insert_iterator<Container>
inserter(Container& x, Iterator i) {
    typedef typename Container::iterator iter;
    return insertor<Container>(x, iter(i));
}

// 这个adapter将迭代器的赋值操作改为insert操作,并且移动迭代器。如此实现表面赋值,实际上insert
template<class Container>
class insert_iterator {
protected:
    Container* constainer;  // 底层容器
    typename Container::iterator iter;
public:
    typedef output_iterator_tag iterator_category; // 标出迭代器类型
    insert_iterator(Container& x, typename Container::iterator i)
        : container(&x), iter(i) {}
    insert_iterator<Container>&
    operator==(const typename Container::value_type& value) {
        iter = container->insert(iter, value); // 插入元素
        ++iter; // 令迭代器永远跟着target移动
        return *this;
    }
}

copy 函数只是按照指针位置逐个空间赋值,inserter 保证每个插入的元素都存储在新申请的空间

ostream_iterator

重写了 operator== 方法

vector<int> myvector{1,2,3,4,5};
ostream_iterator<int> out_it(cout, ",");
copy(myvector.begin(), myvector.end(), out_it);
// 输出 "1,2,3,4,5,"


template<class T, class charT=char, class traits=char_traits<charT>>
class ostream_iterator : public iterator<output_iterator_tag, void, void, void, void> {
    basic_ostream<charT, traits> *out_stream;
    const charT *delim;
public:
    typedef charT char_type;
    typedef traits traits_type;
    typedef basic_ostream<charT, traits> ostream_type;
    ostream_iterator(ostream_type& s) : out_stream(&s), delim(0) {}
    ostream_iterator(ostream_type& s, const charT *delimiter) 
        : out_stream(&s), delim(delimiter) {}
    ostream_iterator(const ostream_iterator<T, charT, traits>& x) 
        : out_stream(x.out_stream), delim(x.delim) {}
    ~ostream_iterator() {}
    // 操作符重载使得赋值变为输出
    ostream_iterator<T, charT, traits>& operator= (const T& value) {
        *out_stream << value;
        if (delim != 0) 
            *out_stream << delim;
        return *this;
    }
    
    ostream_iterator<T, charT, traits>& operator*() { return *this; }
    ostream_iterator<T, charT, traits>& operator++() { return *this; }
    ostream_iterator<T, charT, traits>& operator++(int) { return *this; }
};

istream_iterator

istream_iterator<double> eos; // 没有参数的空istream用于作标兵
// 初始化调用++操作,读取数据
istream_iterator<double> iit(cin);
if (iit != eos)
    value1 = *iit;
// 读取数据
++iit;
if (iit != eos)
    value2 = *iit;

istream_iterator<int> iit(cin), eos;
copy(iit, eos, inserter(c, c.begin()));

template<class T, class charT=char, class traits=char_traits<charT>, class Distance=ptrdiff_t>
class istream_iterator : public iterator<input_iterator_tag, T, Distance, const T*, const T&> {
    basic_istream<charT, traits> *in_stream;
    T value;
public:
    typedef charT char_type;
    typedef traits traits_type;
    typedef basic_istream<charT, traits> istream_type;
    
    istream_iterator() : in_stream(0) {}
    istream_iterator(istream_type& s) : in_stream(&s) { ++*this; }
    istream_iterator(const istream_type<T, charT, traits, Distance>& x) 
        : in_stream(x.in_stream), value(x.value) {}
    ~istream_iterator() {}
    
    const T& operator*() const { return value; }
    
    
    const T* operator->() const { return &value; }
    // ++ 操作会导致读取数据
    istream_iterator<T, charT, traits, Distance>& operator++() {
        if (in_stream && !(*in_stream >> vlaue))
            in_stream = 0;
        return *this;
    }
    istream_iterator<T, charT, traits, Distance> operator++(int) {
        istream_iterator<T, charT, traits, Distance> tmp = *this;
        ++*this;
        return tmp;
    }
};

标准库之内 STL之外

万用 Hash Function

class CustomerHash {
public:
    // 自定义求hash的类函数
    size_t operator()(const Customer& c) const {
        return hash_val(c.xxx, c.yyy, c.zzz);//没有传入 seed,调用下方第一个函数
    }
};

/************    万能的哈希函数        ***********/
template<typename... Type>
inline size_t hash_val(const Types&... args) {
    size_t seed = 0;
    hash_val(seed, args...);
    return seed; // 返回值为 seed
}

template<typename T, typename... Type>//逐一取出 value,将n个变为 1 + (n-1) 个 variadic templates
inline size_t hash_val(size_t& seed, const Types&... args) {
    hash_combine(seed, val);
    hash_val(seed, args...);
}

template<typename T>//处理只有一个 value 的情况
inline void hash_val(size_t& seed, const T& val) {
    hash_combine(seed, val);
}

template<typename T>
inline void hash_combine(size_t& seed, const T& val) {
    seed ^= std::hash<T>()(val) + 0x9e377b9 + (seed<<6) + (seed>>2);
}

标准库提供

#include <hash_fun.h>
hash 类函数

Tuple

一组类型数据的组成的元组,重写了 < = 等操作符

使用:

tuple<string, int, int, complex<double>> t;
sizeof(t); // 32

tuple<int, float, string> t1(41, 6.3, "nico");
cout << "t1: " << get<0>(t1) << get<1>(t1) << get<2>(t2) << endl;

auto t2 = make_tuple(22, 44, "str");

get<1>(t1) = get<1>(t2);

int i; float f; string s;
tie(i, f, s) = t1; // 一次性将tuple的元素拿出,赋值到各个变量

 

实现

template<typename... Values> class tuple;
template<> class tuple<> {};

template<typename Head, typename... Tail> 
class tuple<Head, Tail> : private tuple<Tail...> {
    typedef tuple<Tail...> inherited;
public:
    tuple() {}
    tuple(Head v, Tail... vtail) : m_head(v), inherited(vtail...) {}
    
    typename Head::type head() { return m_head; }
    inherited& tail() { return *this; } // 通过强转去除头元素
protected:
    Head m_head; // 在初始化时赋值头元素
};

tuple<int, float, string> -继承-> tuple<float, string> -继承-> tuple<string> -继承-> tuple<>

type traits

类型萃取 在编译期计算、查询、判断、转换和选择,增强了泛型编程的能力,使得我们在编译期就能做到优化改进甚至排错

template<typename type>//泛化
struct __type_traits {
    typedef __true_type this_dummy_member_must_be_first;
    typedef __false_type has_trival_default_constructor;
    typedef __false_type has_trival_copy_constructor;
    typedef __false_type has_trival_assignment_operator;
    typedef __false_type has_trival_destructor;
    typedef __false_type is_POD_type; // POD Plain Old Data,c语言种struct,只有数据没有函数
};

template<>//特化
struct __type_traits<int> {
    typedef __true_type has_trival_default_constructor;//表示默认构造函数不重要
    typedef __true_type has_trival_copy_constructor;
    typedef __true_type has_trival_assignment_operator;
    typedef __true_type is_POD_type; // POD Plain Old Data
};

// 对于自定义类需要自定义特化模板
// 通过获取 __type_traits<Foo>::has_trival_destructor 的值得知类的析构函数是否重要,并决定出最佳的操作

is_void 源码

template<typaname>
struct __is_void_helper : public false_type {};

template<>
struct __is_void_helper<void> : public true_type {};

template<typename _Tp>
struct is_void : public __is_void_helper<typename remove_cv<_Tp>::type>::type {};

// remove_cv
template<typename _Tp>
struct remove_cv {
    typedef typename remove_const<typename remove_volatile<_Tp>::type>::type type;
};
// remove_const
template<typename _Tp>
struct remove_const { typedef _Tp type; };
template<typename _Tp>
struct remove_const<_Tp const> { typedef _Tp type; };
// remove_volatile
template<typename _Tp>
struct remove_volatile { typedef _Tp type; };
template<typename _Tp>
struct remove_volatile { typedef _Tp type; };

// add_const
template<typename _Tp>
struct add_const { typedef _Tp const type; };
posted @ 2022-06-16 09:33  某某人8265  阅读(155)  评论(0编辑  收藏  举报