C++学习笔记:函数绑定器(bind用法)

绑定器bind总览

C++学习笔记:函数绑定器(bind用法)_第1张图片
bind用于绑定可调用 (Callable) 对象(函数对象、指向函数指针、到函数引用、指向成员函数指针或指向数据成员指针)和其参数。返回值为绑定成功后的函数对象。
在正式开始绑定器前,先进行一下知识补充。

补充1:函数对象

函数对象是可以以函数方式与()结合使用的任意对象,包括:(functor-仿函数)
1、函数名;
2、指向函数的指针;
3、重载了()操作符的类对象(即定义了函数operator()()的类)。C++学习笔记:函数绑定器(bind用法)_第2张图片
转自:https://www.jianshu.com/p/1c986b510cff

补充2:一元函数以及一元断言/一元谓词

一元函数:函数的参数只有一个;
一元断言/一元谓词:函数的参数只有一个,并且返回类型是bool类型。

二元函数:函数的参数有两个;
二元断言/二元谓词:函数的参数两个,并且返回类型是bool类型。

//一元函数
int func(int b) { return b; }
//一元断言/一元谓词
bool func(int a) { return a>1; }

对于一元和二元断言,可以使用bind1st与bind2nd绑定。

template< class F, class T > std::binder1st<F> bind1st( const F& f, const T& x ); 
template< class F, class T > std::binder2nd<F> bind2nd( const F& f, const T& x );

#include 
#include 
#include 
#include 
#include 

using std::cout;
using std::endl;
using std::remove_if;
using std::copy;
using std::vector;
using std::ostream_iterator;

void test()
{
    
    vector<int> number = { 1, 5, 7, 9, 4, 6, 2, 8 };
    //copy函数将容器拷贝到输出迭代器中。
    copy(number.begin(), number.end(), ostream_iterator<int>(cout, " "));
    cout << endl;

    //remove_if仅仅将符合条件的元素放到容器头部排列,返回一个指向需删除元素的位置的迭代器。故还需要erase将无关元素删除。
    number.erase(remove_if(number.begin(), number.end(), bind2nd(std::greater<int>(), 4)), number.end());
    //auto it = remove_if(number.begin(), number.end(), bind2nd(std::greater(), 4));
    //number.erase(it, number.end());

    copy(number.begin(), number.end(), ostream_iterator<int>(cout, " "));
    cout << endl;
	
	/*
	1 5 7 9 4 6 2 8
	1 4 2
	*/
}


int main(int argc, char** argv)
{
    test();
    return 0;
}


补充3:占位符

头文件#include ,形式为std::placeholders::_n,占位符整体代表的是形参的位置,占位符中的数字代表的是实参的位置。

void func4(int x1, int x2, int x3, const int& x4, int x5)
{
    cout << "x1 = " << x1 << endl
         << "x2 = " << x2 << endl
         << "x3 = " << x3 << endl
         << "x4 = " << x4 << endl
         << "x5 = " << x5 << endl;
}

void test3()
{
    int number = 10;
    //bind采用的是值传递
    //ref = reference, 引用的包装器
    //cref = const reference, 引用的包装器
    auto f = bind(func4, 1, std::placeholders::_3, std::placeholders::_1, std::cref(number), number);
    number = 30;
    f(50, 40, 60, 70, 90, 80);//没有用到的参数就丢掉
	/*
	x1 = 1 直接传入的参数
	x2 = 60 实参列表第三位
	x3 = 50 实参列表第一位
	x4 = 30 number的引用,后续更改为30
	x5 = 10 传参时number的值
	*/
}

bind详解

bind函数的使用相比于bind1st以及bind2nd更加的具有通用性,因为后者只能绑定一个参数,而bind可以绑定任意个参数。

#include 
#include 

using std::cout;
using std::endl;
using std::bind;
using std::function;

int add(int a, int b)
{
    cout << "int add(int, int)" << endl;
    return a + b;
}

class Example
{
public:
    int add(int a, int b)
    {
        cout << "int Example::add(int, int)" << endl;
        return a + b;
    }

    int data = 100;//C++11的初始化方式
};

void test()
{
    //function与bind结合使用,就可以实现多态
    
    //bind可以绑定add,可以改变函数add的形态(类型)
    //int(int, int)--->int()
    //函数也是有类型的(函数返回类型与函数的参数列表)
    /* auto f = bind(add, 1, 2); */
    function<int()> f = bind(add, 1, 2);
    cout << "f() = " << f() << endl;

    Example ex;
    //成员函数要取地址,需要明确写出取地址符号
    //int(int, int)--->int(int)
    /* auto f2 = bind(&Example::add, ex, 3, 5); */
    function<int()> f2 = bind(&Example::add, ex, 3, 5);
    cout << "f2() = " << f2() << endl;

    //占位符
    //int(int, int)--->int(int)
    /* auto f3 = bind(add, 4, std::placeholders::_1); */
    function<int(int)> f3 = bind(add, 4, std::placeholders::_1);
    cout << "f3(5) = " << f3(5) << endl;

    //bind可以绑定数据成员
    //int data = 100;
    f = bind(&Example::data, &ex);
    cout << "f() = " << f() << endl;
	/*
	int add(int, int)
	f() = 3
	int Example::add(int, int)
	f2() = 8
	int add(int, int)
	f3(5) = 9
	f() = 100
	*/
}

int main(int argc, char** argv)
{
    test();
    return 0;
}

注册和执行回调函数(多态)

#include 
#include 
#include 

using std::cout;
using std::endl;
using std::bind;
using std::function;

class Rectangle
{
public:
    Rectangle(double length = 0, double width = 0)
        : _length(length)
        , _width(width)
    {
        cout << "Rectangle(double  = 0, double = 0)" << endl;
    }

    void display() const
    {
        cout << "Rectangle : ";
    }

    double area() const
    {
        return _length * _width;
    }

    ~Rectangle()
    {
        cout << "~Rectangle()" << endl;
    }
private:
    double _length;
    double _width;

};
class Circle
{
public:
    Circle(double radius = 0)
        : _radius(radius)
    {
        cout << "Ciecle(double)" << endl;
    }

    void print() const
    {
        cout << "Circle: print()" << endl;
    }

    double printArea() const
    {
        return _radius * _radius * 3.1415;
    }

    ~Circle()
    {
        cout << "~Circle()" << endl;
    }
private:
    double _radius;

};
class Triangle
{
public:
    Triangle(double a = 0, double b = 0, double c = 0)
        : _a(a)
        , _b(b)
        , _c(c)
    {
        cout << "Triangle(double  = 0, double = 0, double = 0)" << endl;
    }

    void show() const
    {
        cout << "Triangle: ";
    }

    double showArea() const
    {
        double tmp = (_a + _b + _c) / 2;

        return sqrt(tmp * (tmp - _a) * (tmp - _b) * (tmp - _c));
    }

    ~Triangle()
    {
        cout << "~Triangle()" << endl;
    }
private:
    double _a;
    double _b;
    double _c;
};

class Figure
{
public:
    //重定义
     /* typedef function  DisplayCallback;//C */
    using DisplayCallback = function<void()>;//C++11
    using AreaCallback = function<double()>;
    DisplayCallback _displayCallback;
    AreaCallback _areaCallback;

    //回调函数的注册
    void setDisplayCallback(const DisplayCallback& call)
    {
        _displayCallback = call;
    }

    void setAreaCallback(AreaCallback&& call)
    {
        _areaCallback = std::move(call);
    }

    //回调函数的执行
    void handleDisplayCallback() const
    {
        if (_displayCallback)
        {
            _displayCallback();
        }
    }

    double handleAreaCallback() const
    {
        if (_areaCallback)
        {
            return _areaCallback();
        }
        else
        {
            return 0;
        }
    }
};

void func(const Figure& fig)
{
    fig.handleDisplayCallback();
    cout << "area : " << fig.handleAreaCallback() << endl;
}

int main(void)
{
    Circle circle(10);

    cout << endl;
    Figure fig;
    //通过Figure类对象对不同的类进行托管,Figure类类似多继承中的基类。 
    //bind绑定类内的成员和this指针后,返回绑定成功的类匿名对象,让函数指针_displayCallback指向其匿名对象。
    //之后即可通过函数指针来执行类内的成员函数,达到实现多态的目的。
    fig.setDisplayCallback(bind(&Circle::print, &circle));
    fig.setAreaCallback(bind(&Circle::printArea, &circle));
    func(fig);
    return 0;
}

/*
结果
Ciecle(double)

Circle: print()
area : 314.15
~Circle()
*/

绑定器与继承实现多态的对比

对象关系:
关系为依赖,耦合度低。
关系为继承,耦合度更高。
代码量:
绑定器需要定义一个绑定类来绑定和执行回调函数,且内部需要定义所有可能用到的函数指针类型。每次切换对象,需要重新绑定所有函数,代码过于冗余。
继承仅需要在派生类内重写虚函数,调用时通过基类指针不断变化指向实现多态,个人感觉代码更为简洁。

具体使用哪种方式根据业务需求而定。

成员函数绑定器mem_fn

成员函数是一个受限制的函数,mem_fn可让其不用this指针,直接绑定,功能形同普通函数。

#include 
#include 
#include 
#include 

using std::cout;
using std::endl;
using std::vector;
using std::for_each;

class Number
{
public:
   Number(size_t data = 0) : _data(data){}

   void print() const
   {
       cout << _data << "  ";
   }

   //判断是不是偶数
   bool isEven() const
   {
       return (0 == _data % 2);
   }

   //质数
   bool isPrimer() const
   {
       if (1 == _data)
       {
           return false;
       }

       for (size_t idx = 2; idx != _data / 2; ++idx)
       {
           if (0 == _data % idx)
           {
               return false;
           }
       }

       return true;
   }
private:
   size_t _data;
};

void test()
{
   vector<Number> vec;
   for (size_t idx = 1; idx != 30; ++idx)
   {
       vec.push_back(Number(idx));
   }

   //成员函数是一个受限制的函数
   for_each(vec.begin(), vec.end(), std::bind(&Number::print, std::placeholders::_1));
   /* for_each(vec.begin(), vec.end(), std::mem_fn(&Number::print)); */
   /* for_each(vec.begin(), vec.end(), &Number::print); error*/
   cout << endl;

   vec.erase(remove_if(vec.begin(), vec.end(), std::mem_fn(&Number::isEven)), vec.end());
   for_each(vec.begin(), vec.end(), std::mem_fn(&Number::print));
   cout << endl;

   vec.erase(remove_if(vec.begin(), vec.end(), std::mem_fn(&Number::isPrimer)), vec.end());
   for_each(vec.begin(), vec.end(), std::mem_fn(&Number::print));
   cout << endl;
}

int main(int argc, char** argv)
{
   test();
   return 0;
}

/*
1  2  3  4  5  6  7  8  9  10  11  12  13  14  15  16  17  18  19  20  21  22  23  24  25  26  27  28  29
1  3  5  7  9  11  13  15  17  19  21  23  25  27  29
1  3  9  15  21  25  27
*/

你可能感兴趣的:(c++,学习,开发语言)