1. 程式人生 > >5、【C++ STL】仿函式(函式物件)

5、【C++ STL】仿函式(函式物件)

仿函式(函式物件)

    仿函式又稱函式物件,函式物件首先是一個物件,即某個類的例項。其次,函式物件的行為和函式一致,即是說可以像呼叫函式一樣來使用函式物件,如引數傳遞、返回值等。這種行為是通過過載類的()操作符來實現的。 【示例

class Print
{
public:
    void operator()(int n)
    {
        std::cout<<n<<std::endl;
        return ;
    }
};
int main(int argc, char **argv)
{
    Print print;
    print
(372); print.operator()(372); //~ 顯式呼叫 return 0; }

    其實我們早就開始使用函式物件了,當你寫下sort(v.begin(), v.end())時(假定v是vector),其實呼叫的是sort(v.begin(), v.end(), less()),這樣sort就會將v從小至大排序。若要逆向排序,你就需要顯式地為sort指定一個排序規則,即函式物件greater()。

    less和greater是STL中的兩個模板類,它們使用型別T的<和>操作符。less的一個典型實現可能是這樣的:

template <
class T> class less { public: bool operator()(const T&l, const T&r)const { return l < r; } };
1、函式物件的分類

    根據用途和引數特徵,STL中的函式物件通常分為以下幾類:Predicates, Arithmetic Function Objects, Binders, Negaters, Member Function Adapters, Pointer to Function Adapters。下面逐一介紹,之前得先介紹兩個基類:

template<class Arg, class Res>
struct unary_function //~ 一元函式物件基類
{
   typedef Arg argument_type;
   typedef Res result_type;
};
 
template<class Arg1, class Arg2, class Res>
struct binary_function //~ 二元函式物件基類
{
   typedef Arg1 first_argument_type;
   typedef Arg2 second_argument_type;
   typedef Res  result_type;
};

(1)Predicates

    Predicate是一種函式物件,返回值(應該是operator()的返回值)為布林型,接受一個或者兩個引數。通常用來判斷物件的有效性(一個引數時)或者對兩個物件進行比較(如less)。你可以根據自己的需要定義自己的Predicate,但STL已經定義了一些Predicate,你可以直接使用。

(2)算術運算函式物件

    進行簡單的算術運算,這類函式物件我用的很少,通常是自己定義。

(3)繫結Binders

    有兩種繫結bind1st和bind2nd,它們可以將一個二元函式物件的其中一個引數繫結為某個已知的物件,從而得到一個一元函式物件。例如要在vector v中查詢等於372的值的位置,我可以將372繫結到equal_to()的第一個或者第二個引數:

int main(int argc, char **argv)
{
    vector<int> v;
    for(int i = 0; i < 1000; ++i)
    {
        v.push_back(i);
    }
    vector<int>::iterator it;
    it = find_if(v.begin(), v.end(), bind1st(equal_to<int>(), 372));
    std::cout<<*it<<std::endl;
    return 0;
}

    其實,這裡的bind1st和bind2nd並不是函式物件,只是模板函式而已。這兩個函式分別返回型別為binder1st和binder2nd的函式物件。下面的程式碼,聰明的你肯定一看就懂:

// bind1st
template<class Op> 
class binder1st : public unary_function
                  <typename Op::second_argument_type,
                   typename Op::result_type>
{
   Op op_;
   typename Op::first_argument_type first_arg_;
 
   public:
      binder1st(const Op& op,
                const typename Op::first_argument_type&
                first_arg) : op_(op),
               first_arg_(first_arg) {}
 
   typename Op::result_type operator()
      (const typename Op::second_argument_type& arg) const
   {
      return op_(first_arg_, arg);
   }
};
 
template<class Op, class Arg>
inline binder1st<Op> bind1st(const Op& op,
                             const Arg& arg)
{
   return binder1st<Op>(op, arg);
}
 
// bind2nd
template<class Op>
class binder2nd : public unary_function
   <typename Op::first_argument_type,
    typename Op::result_type>
{
   Op op_;
   typename Op::second_argument_type second_arg_;
 
   public:
      binder2nd(const Op& op,
                const typename Op::second_argument_type&
                                   second_arg) : op_(op),
                                   second_arg_(second_arg) {}
 
   typename Op::result_type operator()(const typename
      Op::argument_type& arg) const
   {
      return op_(arg, second_arg_);
   }
};
 
template<class Op, class Arg>
inline binder2nd<Op> bind2nd(const Op& op,
                             const Arg& arg)
{
   return binder2nd<Op>(op, arg);
}

(4)Negaters

    Negater是針對Predicate設計的,它簡單的將Predicate的返回值取反。有兩個Negater,not1和not2,分別對一元和二元Predicate取反。

(5)Member Function Adapters

    有時候,你可能想讓演算法呼叫容器元素的成員函式,而不是外部函式。因為外部無法改變物件內的狀態,且內部函式通常具有更高的效率。例如swap(string, string)總是沒有string.swap(string)快速。又比如sort無法對list進行排序,這時候只能使用list.sort()來給連結串列排序。這時候就需要使用一定的方法將物件內部的函式“變成”函式物件,這樣的函式物件叫做成員函式介面卡,其實前面的binder也是一種介面卡。看下面的例子:

int main(int argc, char **argv)
{
    vector<list<int> > v;
    v.push_back(list<int>());
    vector<list<int> >::iterator it;
    for(it = v.begin(); it != v.end(); ++it)
    {
        for(int i = 0; i < 20; ++i)
        {
            (*it).insert((*it).begin(), i);
        }
    }
    for_each(v.begin(), v.end(), std::mem_fun_ref(&list<int>::sort));
    for(it = v.begin(); it != v.end(); ++it)
    {
        for(list<int>::iterator lt; lt != (*it).end(); ++lt)
        {
            std::cout<<*lt<<std::endl;
        }
    }
    return 0;
}

    上面的例子中,遍歷vector<list >並對連結串列進行排序。其中使用的是成員函式介面卡mem_fun_ref,它返回的函式物件會以list物件的引用為引數。另外一個mem_fun則是以指向list物件的指標為引數。