[GeekBand] STL 仿函數入門詳解

本文參考文獻::GeekBand課堂內容,授課老師:張文傑ios

            :C++ Primer 11 中文版(第五版) page 37算法

                   :網絡資料: 葉卡同窗的部落格  http://www.leavesite.com/數組

                                                          

前言:本文主要經過關聯容器set解釋下仿函數的實現及工做原理。網絡

 

1、STL六大組件簡介

一、Containers(容器):各類數據結構,如Vector,List,Deque,Set,Map,用來存放數據
二、Algorithms(算法):如、 Sort,Search。
三、Iterators(迭代器):扮演容器與算法之間的膠合劑,是所謂的「泛型指針」,
四、Functors(仿函數): 行爲相似函數,可做爲算法的某種策略(Policy
五、Adapters(配接器、適配器):一種用來修飾容器(Containers)或仿函數(Functors)或迭代器(Iterators)接口的東西,
六、Allocators(分配器):負責空間配置與管理數據結構

具體的關係如圖,之後會更新各個組件的關係,本文重點關注下仿函數相關知識要點函數

 

                                                 

 

2、函數指針與仿函數區別

一、函數指針,就是一個指針,它指向一個函數。運用時至關於回調了這個函數。spa

示例以下:.net

      這裏經過fp指針,指向了PrintTest函數,這裏 fp();就是調用 PrintTest函數,打印了Hello World 指針

      其實仿函數就相似於函數指針的使用。下面會經過例子來講明。code

#include "stdafx.h"
typedef void(*Test)(char* s);
void printTest(char* s);
int main(int argc, char* argv[])
{
    Test fp;      //一般是用宏Test來聲明一個函數指針fp
    fp = printTest;
    fp("Hello World!\n");
    return 0;
}
void printTest(char* src)
{
    printf(src);
}

 

結果以下:

     

 

再看一遍仿函數的定義:

     Functors(仿函數)something that performs a function, 即相似函數的東西,那麼它是什麼樣子的,又是如何實現的呢?

2.1 爲何使用仿函數

使用仿函數可使迭代和計算分離開來。於是你的functor能夠應用於不一樣場合,在STL的算法中就大量使用了functor

set<Programmer, ProgrammerIdGreater> set1(programmerArray, programmerArray + 6);
    cout << "Traversal And Sort The Programmer by Number " << endl;
    //print
    for_each(set1.begin(), set1.end(), mem_fn(&Programmer::Print));
    cout << "\n" << endl;

這裏經過set容器,經過 <>來進行了自定義的比較操做,這裏其實是經過適配器調用了重載了仿函數()運算符

下一句for_each這句就更明顯了!這裏經過類中重載()運算符的方法使用了一個函數對象

2.二、仿函數的實現

就是一個類看上去是一個函數,其實現方法就是類中重載了一個operator(),這個類就有了相似函數的行爲,就是一個仿函數類了

//仿函數(理解爲函數指針),對()進行重載,就是一旦用括號了,括號內的內容已經排序完畢
//咱們若是要讓仿函數支持適配器,那麼就必須從binary_function派生出來。
struct ProgrammerIdGreater : public binary_function<Programmer, Programmer, bool> 
{
    bool operator() (const Programmer& PosFront, const Programmer& PosBehind) const
    {

        //降序排列
        //return (PosFront.GetId() <= PosBehind.GetId()) ? false : true;


        //升序排列
        return (PosFront.GetId() < PosBehind.GetId()) ? true : false;
    }
};


//一元的都繼承自unary_function,二元則繼承自binary_function, 由於繼承自這兩個函數的仿函數均定義了相應型別供
//配接時使用,也就具備了配接能力。
/*
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;
};

*/

struct ProgrammerNameComparer : public binary_function<Programmer, Programmer, bool>
{
    bool operator() (const Programmer& PosFront, const Programmer& PosBehind) const 
    {
        //按照名稱降序排列
        //return (PosFront.GetName() <= PosBehind.GetName()) ? false : true;
        //按照名稱升序排列
        return (PosFront.GetName() <= PosBehind.GetName()) ? true : false;
    }
};

 

 

3、以Set容器的完整示例

// Programmer.cpp : 定義控制檯應用程序的入口點。
//

#include "stdafx.h"
#include "programmer.h" 
#include <iostream>
#include <set>
#include <algorithm>
#include <functional>
using namespace std;
/*
    set的iterator類型通常是const的引用類型,所以當set保存的是類類型時,對iterator解引用沒法調用類的非const成員。
    解決辦法:1.set中不存儲對象自己,改成存儲對象指針
    利用const_cast<Programmer&> 進行轉型
*/
int main(int argc, char** argv) 
{
    const Programmer programmerArray[6] = 
    {
          Programmer(1, L"Scott Meyers"),
          Programmer(2, L"Martin Fowler"),
          Programmer(3, L"Bill Gates"),
          Programmer(4, L"P.J. Plaught"),
          Programmer(5, L"Stanley B. Lippman"),
          Programmer(6, L"Andrei Alexandrescu"),
    };
    //仿函數ProgrammerIdGreater排序,此時重載了小括號
    //以序列號進行排序
    set<Programmer, ProgrammerIdGreater> set1(programmerArray, programmerArray + 6);
    cout << "Traversal And Sort The Programmer by Number " << endl;
    //print
    for_each(set1.begin(), set1.end(), mem_fn(&Programmer::Print));
    cout << "\n" << endl;


    //查找目標,並將目標修改成David Vandevoorde
    set<Programmer, ProgrammerIdGreater>::iterator it = set1.find(Programmer(3, L"Bill Gates"));
    if (it != set1.end())
    {
        const_cast<Programmer&>(*it).SetName(L"David Vandevoorde");
    }
    cout << "Change The Name Of Third Element And Traversal" << endl;
    //print
    for_each(set1.begin(), set1.end(), mem_fn(&Programmer::Print));
    cout << "\n" << endl;
    

    //此時並無保存到數組programmerArray[6]中
    //按照名稱進行排序
    cout << "Traversal And Sort The Programmer  by Name " << endl;
    set<Programmer, ProgrammerNameComparer> set2(set1.begin(), set1.end());
    //print
    for_each(set2.begin(), set2.end(), mem_fn(&Programmer::Print));
    return 0;
}

 

#ifndef __PROGRAMMER_H__
#define __PROGRAMMER_H__

#include <iostream>
#include <string>
using namespace std;

struct Programmer 
{
public:
    //構造函數
    Programmer(const int id, const wstring name)
        : Id(id), Name(name)
    { 

    }
    void Print() const 
    {
        wcout << L"[" << Id << L"]:" << Name << endl;
    }
    const int GetId() const
    {
        return Id; 
    }
    const wstring& GetName() const 
    {
        return Name;
    }
    void SetName(const wstring& name) 
    {
        Name = name;
    }
private:
    int Id;
    wstring Name;
};


//仿函數(理解爲函數指針),對()進行重載,就是一旦用括號了,括號內的內容已經排序完畢
//咱們若是要讓仿函數支持適配器,那麼就必須從binary_function派生出來。
struct ProgrammerIdGreater : public binary_function<Programmer, Programmer, bool> 
{
    bool operator() (const Programmer& PosFront, const Programmer& PosBehind) const
    {

        //降序排列
        //return (PosFront.GetId() <= PosBehind.GetId()) ? false : true;


        //升序排列
        return (PosFront.GetId() < PosBehind.GetId()) ? true : false;
    }
};


//一元的都繼承自unary_function,二元則繼承自binary_function, 由於繼承自這兩個函數的仿函數均定義了相應型別供
//配接時使用,也就具備了配接能力。
/*
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;
};

*/

struct ProgrammerNameComparer : public binary_function<Programmer, Programmer, bool>
{
    bool operator() (const Programmer& PosFront, const Programmer& PosBehind) const 
    {
        //按照名稱降序排列
        //return (PosFront.GetName() <= PosBehind.GetName()) ? false : true;
        //按照名稱升序排列
        return (PosFront.GetName() <= PosBehind.GetName()) ? true : false;
    }
};

#endif 

 

關於Set容器的使用請參考。非在本文討論範圍內。

       http://blog.csdn.net/lyhvoyage/article/details/22989659

4、總結

仿函數相似函數指針,經過這樣對比來記憶。

仿函數要重載operator()

仿函數都有其型別。所以他能夠將仿函數的型別當template參數傳遞

執行速度,仿函數比函數指針更快。

相關文章
相關標籤/搜索