zoukankan      html  css  js  c++  java
  • Understanding Function Objects

    Function objects or functors might sould exotic or intimidating, but they are entities of C++ that you have probably seen if not also used, without having realized it.

    The Concept of Function Objects and Predicate

    On a conceptual level, function objects are objects that work as function. On an implementation level, however, function objects are objects of a class that implements operator(). Although functions and function-pointers can also be classified as function objects, it is the capability of an object of a class that implements operator() to carry state(that is, values in member attributes of the class) that makes it useful with standard template library algorithms.

    Function objects as typically used by a C++ programmer working with STL are classifiable into the following type:

    • Unary function ---- A function called with one argument. When a unary function returns a bool, it is called a predicate.
    • Binary function ---- A function called with two arguments. Weh a binary function returns a bool, it is called a binary predicte.
    Function objects that return a boolean type naturally find use in algorithms that need decision-making. A function object that combines two function objects is called an adaptive function object.
    Typical Applications of Function Objects
    it is possible to explain function objects over pages and pages of theoretical explanations. It is also possible to understand how they look and work via tiny sample applications. Let's take the practical approach and dive straight into the world of C++ programming with function objects of functors.
    Unary Functions
    Functions that operate on a single parameter are unary functions. A unary function can do something very simple, for example displays an element on the screen. This can be programmed as
    View Code
    #include <algorithm>
    #include
    <iostream>
    #include
    <vector>
    #include
    <list>

    using namespace std;

    // struct that behaves as a unary function
    template <typename elementType>
    struct DisplayElement
    {
    void operator () (const elementType& element) const
    {
    cout
    << element << ' ';
    }
    };

    int main ()
    {
    vector
    <int> vecIntegers;

    for (int nCount = 0; nCount < 10; ++ nCount)
    vecIntegers.push_back (nCount);

    list
    <char> listChars;

    for (char nChar = 'a'; nChar < 'k'; ++nChar)
    listChars.push_back (nChar);

    cout
    << "Displaying the vector of integers: " << endl;

    // Display the array of integers
    for_each ( vecIntegers.begin () // Start of range
    , vecIntegers.end () // End of range
    , DisplayElement <int> () ); // Unary function object

    cout
    << endl << endl;
    cout
    << "Displaying the list of characters: " << endl;

    // Display the list of characters
    for_each ( listChars.begin () // Start of range
    , listChars.end () // End of range
    , DisplayElement <char> () );// Unary function object

    return 0;
    }

     

    The real advantage of using a function object implemented in a struct becomes apparent when you are able to use the object of the struct to store information. This is something FuncDisplayElement cannot do the way a struct can coz a struct can have member attributes, other than the operator().  A slightly modified version that makes use of member attributes would be:

    View Code
    template <typename elementType>
    struct DisplayElementKeepCount
    {
    // Hold the count in a member variable
    int m_nCount;

    // Constructor
    DisplayElementKeepCount ()
    {
    m_nCount
    = 0;
    }

    // Display the element, hold count!
    void operator () (const elementType& element)
    {
    ++ m_nCount;
    cout
    << element << ' ';
    }
    };

      

     

    The advantage of using such function objects that can also store state is seen in:

    View Code
    #include <algorithm>
    #include
    <iostream>
    #include
    <vector>
    #include
    <list>

    using namespace std;

    template
    <typename elementType>
    struct DisplayElementKeepCount
    {
    // Hold the count in a member variable
    int m_nCount;

    // Constructor
    DisplayElementKeepCount ()
    {
    m_nCount
    = 0;
    }

    // Display the element, hold count!
    void operator () (const elementType& element)
    {
    ++ m_nCount;
    cout
    << element << ' ';
    }
    };

    int main ()
    {
    vector
    <int> vecIntegers;

    for (int nCount = 0; nCount < 10; ++ nCount)
    vecIntegers.push_back (nCount);

    cout
    << "Displaying the vector of integers: " << endl;

    // Display the array of integers
    DisplayElementKeepCount <int> mResult;
    mResult
    = for_each ( vecIntegers.begin () // Start of range
    , vecIntegers.end () // End of range
    , DisplayElementKeepCount <int> () );// function object

    cout
    << endl << endl;

    // Use the state stores in the return value of for_each!
    cout << "'" << mResult.m_nCount << "' elements were displayed!" << endl;

    return 0;
    }

      

    Unary Predicate

    A unary function that returns a bool is a predicate. Such functions help make decisions for STL algorithms.

    View Code
    template <typename numberType>
    struct IsMultiple
    {
    numberType m_Divisor;

    // divisorialize the divisor
    IsMultiple (const numberType& divisor)
    {
    m_Divisor
    = divisor;
    }

    // The comparator of type: bool f(x)
    bool operator () (const numberType& element) const
    {
    // Check if the dividend is a multiple of the divisor
    return ((element % m_Divisor) == 0);
    }
    };

    We use this predicat in the below code:

    View Code
    #include <algorithm>
    #include
    <vector>
    #include
    <iostream>

    using namespace std;

    template
    <typename numberType>
    struct IsMultiple
    {
    numberType m_Divisor;

    // divisorialize the divisor
    IsMultiple (const numberType& divisor)
    {
    m_Divisor
    = divisor;
    }

    // The comparator of type: bool f(x)
    bool operator () (const numberType& element) const
    {
    // Check if the dividend is a multiple of the divisor
    return ((element % m_Divisor) == 0);
    }
    };

    int main ()
    {
    vector
    <int> vecIntegers;

    cout
    << "The vector contains the following sample values: ";

    // Insert sample values: 25 - 31
    for (int nCount = 25; nCount < 32; ++ nCount)
    {
    vecIntegers.push_back (nCount);
    cout
    << nCount << ' ';
    }

    cout
    << endl;

    // Find the first element that is a multiple of 4 in the collection
    vector <int>::iterator iElement;
    iElement
    = find_if ( vecIntegers.begin ()
    , vecIntegers.end ()
    , IsMultiple
    <int> (4) ); // Unary predicate initialized to 4

    if (iElement != vecIntegers.end ())
    {
    cout
    << "The first element in the vector divisible by 4 is: ";
    cout
    << *iElement << endl;
    }

    return 0;
    }

     

    Binary Functions

    Functions of type f(x, y) are particularly useful when they return a value based on the input supplied. Such binary functions can be used for a host of arithmetic activity that involves two operands, such as addition, multiplication, subtraction, and the like. A sample binary function that returns the multiple of input arguments can be written as: 

    View Code
    template <typename elementType>
    class CMultiply
    {
    public:
    elementType
    operator() (const elementType & elem1, const elementType & elem2)
    {
    return (elem1 * elem2);
    }
    }

      

    The code below demonstrates the usage of such binary functions in std::transform.

    View Code
    #include <vector>
    #include
    <iostream>
    #include
    <algorithm>

    template
    <typename elementType>
    class CMultiply
    {
    public:
    elementType
    operator () (const elementType& elem1,
    const elementType& elem2)
    {
    return (elem1 * elem2);
    }
    };

    int main ()
    {
    using namespace std;

    // Create two sample vector of integers with 10 elements each
    vector <int> vecMultiplicand, vecMultiplier;

    // Insert sample values 0 to 9
    for (int nCount1 = 0; nCount1 < 10; ++ nCount1)
    vecMultiplicand.push_back (nCount1);

    // Insert sample values 100 to 109
    for (int nCount2 = 100; nCount2 < 110; ++ nCount2)
    vecMultiplier.push_back (nCount2);

    // A third container that holds the result of multiplication
    vector <int> vecResult;

    // Make space for the result of the multiplication
    vecResult.resize (10);

    transform ( vecMultiplicand.begin (),
    // range of multiplicands
    vecMultiplicand.end (), // end of range
    vecMultiplier.begin (), // multiplier values
    vecResult.begin (), // range that holds result
    CMultiply <int> () ); // the function that multiplies

    cout
    << "The contents of the first vector are: " << endl;
    for (size_t nIndex1 = 0; nIndex1 < vecMultiplicand.size (); ++ nIndex1)
    cout
    << vecMultiplicand [nIndex1] << ' ';
    cout
    << endl;

    cout
    << "The contents of the second vector are: " << endl;
    for (size_t nIndex2 = 0; nIndex2 < vecMultiplier.size (); ++nIndex2)
    cout
    << vecMultiplier [nIndex2] << ' ';
    cout
    << endl << endl;

    cout
    << "The result of the multiplication is: " << endl;
    for (size_t nIndex = 0; nIndex < vecResult.size (); ++ nIndex)
    cout
    << vecResult [nIndex] << ' ';

    return 0;
    }

      

    Binary Predicate

    A function that accpets two arguments and returns a bool is a binary predicate. Such functions find application in STL functions such as std::sort.

    View Code
    #include <algorithm>
    #include
    <string>
    using namespace std;

    class CCompareStringNoCase
    {
    public:
    bool operator () (const string& str1, const string& str2) const
    {
    string str1LowerCase;
    // Assign space
    str1LowerCase.resize (str1.size ());
    // Convert every character to the lower case
    transform ( str1.begin (), str1.end ()
    , str1LowerCase.begin (), tolower );

    string str2LowerCase;
    str2LowerCase.resize (str2.size ());

    transform ( str2.begin (), str2.end ()
    , str2LowerCase.begin (), tolower);

    return (str1LowerCase < str2LowerCase);
    }
    };

      

    Binary predicate used in associative containers such as std::set:

    View Code
    #include <set>
    #include
    <iostream>
    #include
    <algorithm>
    #include
    <string>
    using namespace std;

    class CCompareStringNoCase
    {
    public:
    bool operator () (const string& str1, const string& str2) const
    {
    string str1LowerCase;
    // Assign space
    str1LowerCase.resize (str1.size ());
    // Convert every character to the lower case
    transform ( str1.begin (), str1.end ()
    , str1LowerCase.begin (), tolower );

    string str2LowerCase;
    str2LowerCase.resize (str2.size ());

    transform ( str2.begin (), str2.end ()
    , str2LowerCase.begin (), tolower);

    return (str1LowerCase < str2LowerCase);
    }
    };


    int main ()
    {
    typedef
    set <string, CCompareStringNoCase> SET_NAMES;

    // Define a set of string to hold names
    SET_NAMES setNames;

    // Insert some sample names in to the set
    setNames.insert ("Tina");
    setNames.insert (
    "jim");
    setNames.insert (
    "Jack");
    setNames.insert (
    "Sam");

    cout
    << "The sample names in the set are: " << endl;

    // Display the names in the set
    SET_NAMES::const_iterator iNameLocator;
    for ( iNameLocator = setNames.begin ()
    ; iNameLocator
    != setNames.end ()
    ;
    ++ iNameLocator )
    cout
    << *iNameLocator << endl;

    cout
    << "Enter a name you wish to search the set for: ";
    string strUserInput;
    cin
    >> strUserInput;

    SET_NAMES::iterator iNameFound
    = setNames.find (strUserInput);

    if (iNameFound != setNames.end ())
    cout
    << "'" << *iNameFound << "' was found in the set" << endl;
    else
    cout
    << "Name '" << strUserInput << "' was not found in the set";

    return 0;
    }

      

      

      

     

  • 相关阅读:
    Android 数据库框架 DBFlow 的使用
    Android进阶AIDL使用自定义类型
    Android进阶之AIDL的使用详解
    RecyclerView实现拖动排序和滑动删除功能
    RecyclerView的刷新分页
    RecyclerView 的 Item 的单击事件
    RecyclerView 的简单使用
    AutoCompleteTextView的简单使用
    Spinner的简单实用
    黎曼猜想
  • 原文地址:https://www.cnblogs.com/DanielZheng/p/2136869.html
Copyright © 2011-2022 走看看