zoukankan      html  css  js  c++  java
  • 【C++ 系列笔记】06 C++ STL

    STL

    函数对象(仿函数)

    函数对象是重载函数调用操作符的类实例。

    函数对象根据参数数量可被称为 N 元仿函数,N 为函数对象调用所需参数个数。

    重载 () 符号:

    class Type {
       public:
        void operator()(/* args */);
    };
    

    调用:

    Type objFun();
    objFun();
    
    // 匿名对象
    Type()();
    

    函数对象可以实现闭包:

    class Counter {
       private:
       	long count; 
       public:
        Counter():a(0){}
        void operator()(){
            return count++;
        }
    };
    
    int main(){
        Counter add();
        add();
    }
    

    谓词

    在离散数学中,谓词指命题中表示客体关系的词。如 1 小于 2 这个命题中,小于就是一个谓词,且是一个二元谓词,因为它连接了两个客体 12

    类似的,我们称 返回值为 bool 的函数对象 为谓词,且 N 元谓词指 接收 N 个参数的谓词

    在 STL 中,谓词用以改变算法的策略。

    例如从大到小排序:

    class increase{
       public:
        bool operator()(int a, int b){
            return a > b;
        }
    }
    // ...
    sort(v.begin(), v.end(), increase());
    

    内建函数对象

    即 STL 预先提供的谓词,这些谓词包含在一个头文件中functional

    #include<functional>
    

    常用谓词列举:

    • 算数类

      // 除 negate 为一元谓词外,其余全为二元谓词
      template<class T> T plus<T>        // 加法仿函数
      template<class T> T minus<T>       // 减法仿函数
      template<class T> T multiplies<T>  // 乘法仿函数
      template<class T> T divides<T>     // 除法仿函数
      template<class T> T modulus<T>     // 取模仿函数
      template<class T> T negate<T>      // 取反仿函数
      
    • 关系类

      // 均为二元谓词
      template<class T> bool equal_to<T>       // 等于
      template<class T> bool not_equal_to<T>   // 不等于
      template<class T> bool greater<T>        // 大于
      template<class T> bool greater_equal<T>  // 大于等于
      template<class T> bool less<T>           // 小于
      template<class T> bool less_equal<T>     // 小于等于
      
    • 逻辑类

      // 除 not 为一元谓词外,其余均为二元谓词
      template<class T> bool logical_and<T>  // 逻辑与
      template<class T> bool logical_or<T>   // 逻辑或
      template<class T> bool logical_not<T>  // 逻辑非
      

    使用示例:

    sort从大到小排序为例:

    sort(v.begin(), v.end(), greater<int>());
    

    适配器

    感觉适配器更像是一种包装器,用于包装函数对象,完成不同的扩展功能。

    函数对象适配器

    bind1st bind2nd

    函数对象适配器用于扩展函数对象的功能,提供额外传入参数的方法。

    例如,我现在有一个一元谓词:

    class Pred {
       public:
        bool operator()(int a) {
            // ...
        }
    };
    

    现在调用某算法,该算法会使用这个一元谓词。

    algorithm(..., ..., Pred());
    

    但与此同时,我们希望这个谓词可以额外实现一个功能,而这个功能依赖一个临时传入的参数。

    于是改了谓词的实现:

    bool operator()(int a, string b){
        // ...
    }
    

    但这个新参数b是无法在调用algorithm(..., ..., Pred());时传进来的。

    此时就要用到适配器了。

    基本步骤:

    让这个谓词的类继承一个类模板binary_function<{参数1类型, 参数2类型, 返回值类型}>,同时还要用const修饰该谓词为一个常函数(实际上是重写了父类的一个方法)。

    注意,一元谓词继承unary_function二元谓词继承binary_function

    class Pred: public binary_function<int, string, bool>{
       public:
        bool operator()(int a, string b) const {
            // ...
        }
    };
    

    然后,在调用算法时,

    通过一个函数bind2nd,将第二个参数绑定给适配器,同时将这个参数传入。

    algorithm(..., ..., bind2nd(Pred(), "param");
    

    另外,我们也可以使用函数bind1st,将第一个参数绑定给适配器,此时额外的参数将传入第一个参数的位置,而算法则会使用第二个参数。

    bind1st 不仅可以扩展一元谓词,也可以绑定二元谓词,将其变为一元谓词

    例如下面的代码意为 x > 5 的谓词:

    bind2nd(greater<int>(), 1);
    

    底层实现

    就函数对象适配器来说,如果不了解其实现原理,往往很难一下子记住使用步骤。

    比如,为什么要继承,为什么模板类型列表是<{参数1类型, 参数2类型, 返回值类型}>

    又比如,bind1st做了什么工作?

    由于这学期一直在搞 web 方向的东西,免不了要写 js,写 js 就免不了接触链式作用域。

    这个东西虽然强大灵活,但也很难真正理解和应用。虽然四处踩坑,但也训练了我相应的能力。

    不说废话了,进入正题。

    当我看到bind2nd(Pred(), "param"),就立刻想到了包装,写 js 总免不了要把一些东西包装起来,扩展功能。

    读了一下库里的实现代码,发现它大概是做了这些事情,与我想的差不多:

    返回一个闭包,将函数对象和额外的参数放在外部作用域,当算法调用时,将额外参数传入。

    用 js 描述如下:

    function bind2nd(fun, extraParam){
        return function(param){
            return fun(param, extraParam);
        }
    }
    

    而在 C++ 里,它是返回了一个类的实例,这个实例通过函数对象及额外参数进行构造,随后在里面进行 () 的重写,包装我们的函数对象。

    实际上到这里就已经很清楚了,不过之后我却一直在思考,为什么还有一个继承的步骤,为什么要继承。

    实际上,js 写到这里也就结束了,但 C++ 是静态类型的语言,在 js 中习以为常的随便传参随便返回,什么 undefined、null 等等,在 C++ 中必须要有明确地定义。

    那么实际上,这个继承就是在处理参数类型及返回值类型。

    再次分析了一下实现代码,发现他做了这些工作:

    我们要继承的类binary_function是一个类模板,继承后进行实例化bind2nd(Pred(), "param")

    此时父类通过using别名指定,将参数及返回值类型记录下来,这些被记录下来的值在bind2nd包装时被使用,用作 () 的重写。

    两个类,一个作类型记录,一个作调用包装,就这样配合着完成适配器的工作。

    大概手写一下:

    这是大概是包装类的实现:

    tmelate<class funType>
    class bind2nd {
       public:
        using p1Type = funType::p1Type;
        using p2Type = funType::p2Type;
        using resultType = funType::resultType;
        binary_function(funType& fun, p2Type extraParam): fun(fun), extraParam(extraParam) {};
        resultType operator()(const p1Type& arg) const {
          return fun(arg, value);
        }
       protected:
        funType fun;
        p2Type extraParam; // the right operand
    };
    

    这个大概是我们要继承的类的实现:

    template<class arg1, class arg2, class result>
    class binary_function {
       public:
        using p1Type = typename arg1;
        using p2Type = typename arg2;
        using resultType = typename result; 
    };
    

    取反适配器

    not1 not2

    一元谓词的取反及二元谓词的取反。

    取反就是表面意思,将返回值取反。

    例如,目前有一个 x > 5 的谓词:

    class Pred {
       public:
        bool operator()(int a) const {
            return a > 5;
        }
    };
    

    当该谓词经取反适配器包装后,它就变成了非 x > 5 即 x <= 5 的谓词:

    not1(Pred());
    

    同时需要继承类unary_function

    class Pred: public unary_function<int, bool>;
    

    函数指针适配器

    ptr_fun

    针对普通函数的适配器,将函数指针适配(包装)为一个函数对象,以使用其他针对函数对象的适配器。

    ptr_fun(callback);
    

    可以直接用,也不用做继承,ptr_fun 在底层已经做好了继承。

    成员函数适配器

    mem_fun_ref mem_fun

    用函数对象将成员函数进行包装。

    遍历值容器使用前者,遍历指针容器使用后者,其在包装的时候稍微有一些不同。

    mem_fun_ref(&Type::method);
    

    可以用于批量调用成员方法:

    for_each(v.begin(), v.end(), mem_fun_ref(&Type::method));
    

    算法

    算法主要涉及三个头文件:

    algorithmfunctionalnumeric

    遍历

    遍历for_each

    /*
        遍历算法 遍历容器元素
    	@param _First 开始迭代器
    	@param _Last 结束迭代器
    	@param _Func  函数回调或者函数对象
    	@return 函数对象
    */
    template <class _InIt, class _Fn>
    _Fn for_each(_InIt _First, _InIt _Last, _Fn _Func)
    

    计算accumulate

    /*
    	accumulate 计算容器元素累计总和
    	@param _First 容器开始迭代器
    	@param _Last 容器结束迭代器
    	@param _Val 累加值
    */
    template <class _InIt, class _Ty, class _Fn>
    _Ty accumulate(const _InIt _First, const _InIt _Last, _Ty _Val) 
    
    /*
    	accumulate 计算容器元素累计总和
    	@param _First 容器开始迭代器
    	@param _Last 容器结束迭代器
    	@param _Val 累加值
    	@param _Reduce_op 回调函数或者谓词(@param sum, @param elem, @return sum)传入当前累加值和当前元素,返回累加值
    */
    template <class _InIt, class _Ty, class _Fn>
    _Ty accumulate(const _InIt _First, const _InIt _Last, _Ty _Val, _Fn _Reduce_op) 
    

    增删改、替换、拷贝

    拷贝copy

    /*
    	copy算法 将容器内指定范围的元素拷贝到另一容器中
    	@param _First 容器开始迭代器
    	@param _Last 容器结束迭代器
    	@param _Dest 目标起始迭代器
    */
    template <class _InIt, class _OutIt>
    _OutIt copy(_InIt _First, _InIt _Last, _OutIt _Dest)
    

    有趣的用法:

    #include <iterator>
    // 遍历并输出容器的内容(将容器内容 copy 到流中)
    copy(v.begin(), v.end(), ostream_iterator<int>(cout, " "));
    

    替换replace

    /*
    	replace 将容器内指定范围的旧元素修改为新元素
    	@param _First 容器开始迭代器
    	@param _Last 容器结束迭代器
    	@param _Oldval 旧元素
    	@param _Newval 新元素
    */
    template <class _FwdIt, class _Ty>
    void replace(const _FwdIt _First, const _FwdIt _Last, const _Ty& _Oldval, const _Ty& _Newval)
    

    替换replace_if

    /*
    	replace_if算法 将容器内指定范围满足条件的元素替换为新元素
    	@param _First 容器开始迭代器
    	@param _Last 容器结束迭代器
    	@param _Pred 函数回调或者谓词(返回Bool类型的函数对象)
    	@param _Val 新元素
    */
    template <class _FwdIt, class _Pr, class _Ty>
    void replace_if(const _FwdIt _First, const _FwdIt _Last, _Pr _Pred, const _Ty& _Val) 
    

    交换swap

    /*
    	swap 互换两个容器的元素
    	@param _Left 容器1
    	@param _Right 容器2
    */
    template <class _Fty>
    void swap(function<_Fty>& _Left, function<_Fty>& _Right)
    

    拷贝transform

    一个容器拷贝给另一个容器:

    /*
    	transform 将指定容器区间元素搬运到另一容器中
    	注意 : transform 不会给目标容器分配内存,所以需要我们提前分配好内存
    	@param _First 源容器开始迭代器
    	@param _Last 源容器结束迭代器
    	@param _Dest 目标容器开始迭代器
    	@param _Func 回调函数或者函数对象
    	@return 返回目标容器迭代器
    */
    template <class _InIt, class _OutIt, class _Fn>
    _OutIt transform(const _InIt _First, const _InIt _Last, _OutIt _Dest, _Fn _Func)
    

    两容器交互拷贝给第三个容器:

    /*
    	transform 将指定容器区间元素搬运到另一容器中
    	注意 : transform 不会给目标容器分配内存,所以需要我们提前分配好内存
    	@param _First1 源容器1开始迭代器
    	@param _Last1 源容器1结束迭代器
    	@param _First2 源容器2开始迭代器
    	@param _Dest 目标容器开始迭代器
    	@param _Func 回调函数或者函数对象
    	@return 返回目标容器迭代器
    */
    template <class _InIt1, class _InIt2, class _OutIt, class _Fn>
    _OutIt transform(const _InIt1 _First1, const _InIt1 _Last1, const _InIt2 _First2, _OutIt _Dest, _Fn _Func) 
    

    拼接merge

    /*
    	merge 容器元素合并,并存储到另一容器中
    	@param _First1 容器1开始迭代器
    	@param _Last1 容器1结束迭代器
    	@param _First2 容器2开始迭代器
    	@param _Last2 容器2结束迭代器
    	@param _Dest 目标容器开始迭代器
    	@param _Pred 回调函数或者函数对象
    */
    template <class _InIt1, class _InIt2, class _OutIt, class _Pr>
    _OutIt merge(_InIt1 _First1, _InIt1 _Last1, _InIt2 _First2, _InIt2 _Last2, _OutIt _Dest, _Pr _Pred)
    

    填充fill

    /*
    	fill 向容器中添加元素
    	@param _First 容器开始迭代器
    	@param _Last 容器结束迭代器
    	@param _Val 填充元素
    */
    template <class _FwdIt, class _Ty>
    void fill(const _FwdIt _First, const _FwdIt _Last, const _Ty& _Val)
    

    查找

    查找find

    /*
    	find 查找元素
    	@param _First 容器开始迭代器
    	@param _Last 容器结束迭代器
    	@param _Val 查找的元素
    	@return 返回查找元素的位置
    */
    template <class _InIt, class _Ty>
    _InIt find(_InIt _First, const _InIt _Last, const _Ty& _Val) 
    

    查找find_if

    /*
    	find_if 条件查找
    	@param _First 容器开始迭代器
    	@param _Last 容器结束迭代器
    	@param  _Pred 回调函数或者谓词(@return bool 找到返回 true)
    	@return 返回查找元素的位置
    */
    template <class _InIt, class _Pr>
    _InIt find_if(_InIt _First, const _InIt _Last, _Pr _Pred)
    

    查找adjacent_find

    /*
    	adjacent_find 查找相邻重复元素
    	@param _First 容器开始迭代器
    	@param _Last 容器结束迭代器
    	@param  _Pred 回调函数或者谓词 (@return bool 找到返回 true)
    	@return 返回相邻元素的第一个位置的迭代器
    */
    template <class _FwdIt, class _Pr>
    _FwdIt adjacent_find(const _FwdIt _First, _FwdIt _Last, _Pr _Pred) 
    
    template <class _FwdIt, class _Pr>
    _FwdIt adjacent_find(const _FwdIt _First, _FwdIt _Last) 
    

    查找binary_search

    二分查找,要求容器必须有序。

    /*
    	binary_search算法 二分查找法
    	注意: 在无序序列中不可用
    	@param _First 容器开始迭代器
    	@param _Last 容器结束迭代器
    	@param _Val 查找的元素
    	@param _Pred 回调函数或者谓词 (@param value 查找的元素 @param preVal 当前元素 @return bool 找到返回 true)
    	@return bool 是否找到
    */
    template <class _FwdIt, class _Ty, class _Pr>
    bool binary_search(_FwdIt _First, _FwdIt _Last, const _Ty& _Val, _Pr _Pred);
    
    template <class _FwdIt, class _Ty, class _Pr>
    bool binary_search(_FwdIt _First, _FwdIt _Last, const _Ty& _Val);
    

    数量count

    /*
    	count 统计元素出现次数
    	@param _First 容器开始迭代器
    	@param _Last 容器结束迭代器
    	@param _Val 查找的值
    	@return 返回元素个数
    */
    template <class _InIt, class _Ty>
    size_t count(const _InIt _First, const _InIt _Last, const _Ty& _Val)
    

    数量count_if

    /*
    	count_if 统计元素出现次数
    	@param _First 容器开始迭代器
    	@param _Last 容器结束迭代器
    	@param _Pred 回调函数或者谓词(@return bool 找到返回 true)
    	@return 返回元素个数
    */
    template <class _InIt, class _Pr>
    size_t count_if(_InIt _First, _InIt _Last, _Pr _Pred)
    

    顺序操作

    排序sort

    /*
    	排序算法 默认从小到大
    	@param _First 源容器开始迭代器
    	@param _Last 源容器结束迭代器
    	@param _Pred 回调函数或者函数对象
    	@return void
    */
    template <class _RanIt, class _Pr>
    void sort(const _RanIt _First, const _RanIt _Last, _Pr _Pred)
    

    洗牌random_shuffle

    /*
    	random_shuffle 对指定范围内的元素随机调整次序
    	@param _First 容器开始迭代器
    	@param _Last 容器结束迭代器
    */
    template <class _RanIt>
    void random_shuffle(_RanIt _First, _RanIt _Last)
    

    翻转reverse

    /*
    	reverse 反转指定范围的元素
    	@param _First 容器开始迭代器
    	@param _Last 容器结束迭代器
    */
    template <class _BidIt>
    void reverse(const _BidIt _First, const _BidIt _Last)
    

    集合操作

    交集运算set_intersection

    /*
    	set_intersection 求两个 set 集合的交集
    	注意:两个集合必须是有序序列
    	@param _First1 容器1开始迭代器
    	@param _Last1 容器1结束迭代器
    	@param _First2 容器2开始迭代器
    	@param _Last2 容器2结束迭代器
    	@param _Dest 目标容器开始迭代器
    	@return 目标容器的最后一个元素的迭代器地址
    */
    template <class _InIt1, class _InIt2, class _OutIt>
    _OutIt set_intersection(_InIt1 _First1, _InIt1 _Last1, _InIt2 _First2, _InIt2 _Last2, _OutIt _Dest) 
    
    /*
    	set_intersection 求两个 set 集合的交集
    	注意:两个集合必须是有序序列
    	@param _First1 容器1开始迭代器
    	@param _Last1 容器1结束迭代器
    	@param _First2 容器2开始迭代器
    	@param _Last2 容器2结束迭代器
    	@param _Dest 目标容器开始迭代器
    	@param _Pred 回调函数或者谓词(@param elem2, @param elem1, @return bool)传入两个元素,相等返回 true
    	@return 目标容器的最后一个元素的迭代器地址
    */
    template <class _InIt1, class _InIt2, class _OutIt, class _Pr>
    _OutIt set_intersection(_InIt1 _First1, _InIt1 _Last1, _InIt2 _First2, _InIt2 _Last2, _OutIt _Dest, _Pr _Pred)
    

    并集运算set_union

    /*
    	set_union 求两个 set 集合的并集
    	注意:两个集合必须是有序序列
    	@param _First1 容器1开始迭代器
    	@param _Last1 容器1结束迭代器
    	@param _First2 容器2开始迭代器
    	@param _Last2 容器2结束迭代器
    	@param _Dest  目标容器开始迭代器
    	@return 目标容器的最后一个元素的迭代器地址
    */
    template <class _InIt1, class _InIt2, class _OutIt, class _Pr>
    _OutIt set_union(_InIt1 _First1, _InIt1 _Last1, _InIt2 _First2, _InIt2 _Last2, _OutIt _Dest, _Pr _Pred)
    
    /*
    	set_union 求两个 set 集合的并集
    	注意:两个集合必须是有序序列
    	@param _First1 容器1开始迭代器
    	@param _Last1 容器1结束迭代器
    	@param _First2 容器2开始迭代器
    	@param _Last2 容器2结束迭代器
    	@param _Dest  目标容器开始迭代器
    	@param _Pred 回调函数或者谓词(@param elem2, @param elem1, @return bool)传入两个元素,相等返回 true
    	@return 目标容器的最后一个元素的迭代器地址
    */
    template <class _InIt1, class _InIt2, class _OutIt, class _Pr>
    _OutIt set_union(_InIt1 _First1, _InIt1 _Last1, _InIt2 _First2, _InIt2 _Last2, _OutIt _Dest, _Pr _Pred)
    

    注意,返回的迭代器不一定是 v.end(),它是集合运算完毕后有效数据的最大上限

    差集运算set_difference

    /*
    	set_difference 求两个 set 集合的差集
    	注意:两个集合必须是有序序列
    	@param _First1 容器1开始迭代器
    	@param _Last1 容器1结束迭代器
    	@param _First2 容器2开始迭代器
    	@param _Last2 容器2结束迭代器
    	@param _Dest  目标容器开始迭代器
    	@return 目标容器的最后一个元素的迭代器地址
    */
    template <class _InIt1, class _InIt2, class _OutIt>
    _OutIt set_difference(_InIt1 _First1, _InIt1 _Last1, _InIt2 _First2, _InIt2 _Last2, _OutIt _Dest)
    
    /*
    	set_difference 求两个 set 集合的差集
    	注意:两个集合必须是有序序列
    	@param _First1 容器1开始迭代器
    	@param _Last1 容器1结束迭代器
    	@param _First2 容器2开始迭代器
    	@param _Last2 容器2结束迭代器
    	@param _Dest  目标容器开始迭代器
    	@param _Pred 回调函数或者谓词(@param elem2, @param elem1, @return bool)传入两个元素,相等返回 true
    	@return 目标容器的最后一个元素的迭代器地址
    */
    template <class _InIt1, class _InIt2, class _OutIt, class _Pr>
    _OutIt set_difference(_InIt1 _First1, _InIt1 _Last1, _InIt2 _First2, _InIt2 _Last2, _OutIt _Dest, _Pr _Pred) 
    
  • 相关阅读:
    机器学习学习笔记之二:决策树
    机器学习学习笔记之一:K最近邻算法(KNN)
    Shell脚本编程中的几个问题
    Linux服务器配置git服务
    Ubuntu下安装IDA pro
    网络扫描(二)
    网络扫描(一)
    Docker学习过程中遇到的问题及解决方法
    CentOS7中升级Docker版本
    解决CentOS无法解析域名的问题
  • 原文地址:https://www.cnblogs.com/gaolihai/p/13149740.html
Copyright © 2011-2022 走看看