zoukankan      html  css  js  c++  java
  • c++11特性与cocos2d-x 3.0之std::bind与std::function

    原文地址:http://www.cnblogs.com/slysky/p/3822640.html

    作者:阳光下的蒲公英

    c++11特性与cocos2d-x 3.0之std::bind与std::function

    昨天同事让帮忙写一小功能,才发现cocos2d-x 3.0 和 cocos2d-x 3.0rc0 差别还是相当大的。

    发现Label这一个控件,3.0就比rc0版本多了一个创建函数,更为关键的是3.0内的Label锚点是在ccp(0.5,0.5),而一直3.0rc0是ccp(0,0)。

    累觉不爱。尽管cocos2d-x改变太快,兼容性一次次的暴露出不足,但是,总归是向好的方向进行。于是下载了3.0来玩玩~

    cocos new 出新的项目之后,仔细阅读代码,才发现了一句3.0区别于2.0的代码:

    2.0内的代码用的不是CC_CALLBACK_1而是menu_selector.

    CC_CALLBACK系列是3.0基于c++11的特性新增的。CC_CALLBACK系列的定义如下:

    1 // new callbacks based on C++112 #define CC_CALLBACK_0(__selector__,__target__, ...) std::bind(&__selector__,__target__, ##__VA_ARGS__)3 #define CC_CALLBACK_1(__selector__,__target__, ...) std::bind(&__selector__,__target__, std::placeholders::_1, ##__VA_ARGS__)4 #define CC_CALLBACK_2(__selector__,__target__, ...) std::bind(&__selector__,__target__, std::placeholders::_1, std::placeholders::_2, ##__VA_ARGS__)5 #define CC_CALLBACK_3(__selector__,__target__, ...) std::bind(&__selector__,__target__, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3, ##__VA_ARGS__)
    
     
    

    可以看出,CC_CALL_BACK系统后的数字,表示函数指针的参数个数。明白了这一点,选择CC_CALLBACK时,就不会出错鸟。

    而看示例代码时,还会发现一个有意思的使用方法:

    1                 listener->onTouchesBegan = CC_CALLBACK_2(Layer::onTouchesBegan, this);
    

    此时不禁要问onTouchesBegan又是啥,为啥不能直接函数指针赋值呢?

    看定义就能明白了

    1     std::function<void(const std::vector<Touch*>&, Event*)> onTouchesBegan;
    

    因为CC_CALLBACK系列是std::bind,而onTouchesBegan是std::function来定义的。那么std::bind和std::function又有什么区别呢?

    有博文说:

    function模板类和bind模板函数,使用它们可以实现类似函数指针的功能,但却比函数指针更加灵活,特别是函数指向类的非静态成员函数时。

    std::function可以绑定到全局函数/类静态成员函数(类静态成员函数与全局函数没有区别),如果要绑定到类的非静态成员函数,则需要使用std::bind。

    标准库函数bind()和function()定义于头文件<functional>中(该头文件还包括许多其他函数对象),用于处理函数及函数参数。

    std::bind绑定器

    • 将函数、成员函数和闭包转成function函数对象
    • 将多元(n>1)函数转成一元函数或者(n-1)元函数。

    bind()接受一个函数(或者函数对象,或者任何你可以通过"(...)"符号调用的事物),生成一个其有某一个或多个函数参数被“绑定”或重新组织的函数对象。(译注:顾名思义,bind()函数的意义就像它的函数名一样,是用来绑定函数调用的某些参数的。)例如:

    1         int f(int, char, double);2         auto ff = bind(f, _1, 'c', 1.2);    // 绑定f()函数调用的第二个和第三个参数,返回一个新的函数对象为ff,它只带有一个int类型的参数3         int x = ff(7);                //  f(7, 'c', 1.2);
    

    参数的绑定通常称为"Currying"(译注:Currying---“烹制咖喱烧菜”,此处意指对函数或函数对象进行加工修饰操作), "_1"是一个占位符对象,用于表示当函数f通过函数ff进行调用时,函数ff的第一个参数在函数f的参数列表中的位置。第一个参数称为"_1", 第二个参数为"_2",依此类推。例如:

    1         int f(int, char, double);2         auto frev = bind(f, _3, _2, _1);        // 翻转参数顺序3         int x = frev(1.2, 'c', 7);            // f(7, 'c', 1.2);

        此处,auto关键字节约了我们去推断bind返回的结果类型的工作。
        我们无法使用bind()绑定一个重载函数的参数,我们必须显式地指出需要绑定的重载函数的版本:

    1         int g(int);2         double g(double);3 4         auto g1 = bind(g, _1);                // 错误:调用哪一个g() ?5         auto g2 = bind( (double(*)(double))g, _1);    // 正确,但是相当丑陋
    
     
     1 void H(int a); 2 //绑定全局函数 3 auto f11 = std::bind(H, std::placeholders::_1); 4 auto的类型实际上是std::function<void(int)> 5  6 //绑定带参数的成员函数 7 std::function<void (char*, int)> f = std::bind(&ReadHandler::ConnectPreProcess, this, std::placeholders::_1, std::placeholders::_1); 8  9 //三元函数转换成一元函数10 int f(int, char, double);11 // 绑定f()函数调用的第二个和第三个参数,12 // 返回一个新的函数对象为ff,它只带有一个int类型的参数13 auto ff = bind(f, _1, ‘c’, 1.2);    14 int x = ff(7);
    

    自己写代码示例如下:

     
    int Func(int x, int y);
    auto bf1 = std::bind(Func, 10, std::placeholders::_1);
    bf1(20); ///< same as Func(10, 20)int HelloWorld::AddFunc( int a, int b )
    {
        return a + b;
    }
    bool HelloWorld::init()
    {
    
        auto bf2 = std::bind(&HelloWorld::AddFunc,this , std::placeholders::_1, std::placeholders::_2 );
        auto result1 = bf2(10, 20); ///< same as a.Func(10, 20)
        std::function< int(int)> bf3 = std::bind(&HelloWorld::AddFunc, this, std::placeholders::_1, 100);
        auto result2 = bf3(10); ///< same as a.Func(10, 100)
    }
    

    上面的例子中,bf1是把一个两个参数普通函数的第一个参数绑定为10,生成了一个新的一个参数的可调用实体体; bf2是把一个类成员函数绑定了类对象,生成了一个像普通函数一样的新的可调用实体; bf3是把类成员函数绑定了类对象和第二个参数,生成了一个新的std::function对象。看懂了上面的例子,下面我们来说说使用bind需要注意的一些事项:

    • (1)bind预先绑定的参数需要传具体的变量或值进去,对于预先绑定的参数,是pass-by-value的
    • (2)对于不事先绑定的参数,需要传std::placeholders进去,从_1开始,依次递增。placeholder是pass-by-reference的
    • (3)bind的返回值是可调用实体,可以直接赋给std::function对象
    • (4)对于绑定的指针、引用类型的参数,使用者需要保证在可调用实体调用之前,这些参数是可用的
    • (5)类的this可以通过对象或者指针来绑定

    std::function

    它是函数、函数对象、函数指针、和成员函数的包装器,可以容纳任何类型的函数对象,函数指针,引用函数,成员函数的指针。
    以统一的方式处理函数、函数对象、函数指针、和成员函数。允许保存和延迟执行函数。

    • 函数和成员函数作为function

    function是一个拥有任何可以以"(...)"符号进行调用的值的类型。特别地,bind的返回结果可以赋值给function类型。function十分易于使用。(译注:更直观地,可以把function看成是一种表示函数的数据类型,就像函数对象一样。只不过普通的数据类型表示的是数据,function表示的是函数这个抽象概念。)例如:

     1 typedef std::function<float (int x, int y)> f ;// 构造一个函数对象,它能表示的是一个返回值为float,两个参数为int,int的函数   2 struct int_div {        // 构造一个可以使用"()"进行调用的函数对象类型   3     float operator() (int x, int y) const { return ((float)x)/y; }; 4 }; 5  6 void HelloWorld::testing() 7 { 8     f f1= int_div();                    // 赋值   9     auto result3 = f1( 10, 2);10 }
    
     

    成员函数可被看做是带有额外参数的自由函数:

     1 struct int_div {        // 构造一个可以使用"()"进行调用的函数对象类型   2     float operator() (int x, int y) const { return ((float)x)/y; }; 3     int int_div_fun( int x ){ return x; }; 4 }; 5 typedef std::function<int (int_div*, int)> f_2; 6  7 bool HelloWorld::init() 8 { 9     f_2 f2 = std::mem_fn(&int_div::int_div_fun);            // 指向成员函数10 11     int_div int_div_object;12     int v = f2(&int_div_object, 5);  // 在对象x上用参数5调用X::foo()13     std::function<int (int)> ff = std::bind( f2, &int_div_object, std::placeholders::_1);    // f的第一个参数是&x14     v = ff(5);                // 调用x.foo(5)15 16 17 }
    

    ps:被vs2012的bug给坑了。因为看网上的代码于是刚开始第9行是这么写的:f_2 f2 = &int_div::int_div_fun; 

    然后就报错误:Error 1 error C2664: 'std::_Func_class<_Ret,_V0_t,_V1_t>::_Set' : cannot convert parameter 1 from '_Myimpl *' to 'std::_Func_base<_Rx,_V0_t,_V1_t> *'

    查了一下,vs2010没有这个编译错误,但是2012有。2012必须得加上std::mem_fn才能编译。

    • 可以用function取代函数指针。因为它可以保存函数延迟执行,所以比较适合作为回调函数,也可以把它看做类似于c#中特殊的委托,只有一个成员的委托。
    • function还可以作为函数入参,这样可以在函数外部控制函数的内部行为了,让我们的函数变得更加灵活。
     1 struct int_div {        // 构造一个可以使用"()"进行调用的函数对象类型   2     float operator() (int x, int y) const { return ((float)x)/y; }; 3     int int_div_fun( int x ){ return x; }; 4  5     int_div( std::function<void()>& f ):m_callback(f){}; 6     void Notify() 7     { 8         m_callback(); 9     }10     std::function<void()> m_callback;11 };
    void Foo(int x, std::function<void(int)>& f)
    {if(x%2==0)
    f(x);
    }
    void G(int x)
    {
    cout<<x<<endl;
    }
    void H(int x)
    {
    cout<<x+2<<endl;
    }
    void TestFoo()
    {
    auto f = std::bind(G, std::placeholders::_1); 
    Foo(4, f);
    //在Foo函数外面更改f的行为
    f = std::bind(H, std::placeholders::_1);
    Foo(4, f);
    }
    

    c++11中推出function是为了泛化函数对象,函数指针,引用函数,成员函数的指针,让我们可以按更统一的方式写出更加泛化的代码;推出bind是为了替换和增强之前标准库的bind1st和bind2st,让我们的用起来更方便!

    更多精品iOS、Cocos、移动设计课程请关注智捷课堂官方网站:                         http://www.zhijieketang.com

                                     
  • 相关阅读:
    大数加法、乘法实现的简单版本
    hdu 4027 Can you answer these queries?
    zoj 1610 Count the Colors
    2018 徐州赛区网赛 G. Trace
    1495 中国好区间 尺取法
    LA 3938 动态最大连续区间 线段树
    51nod 1275 连续子段的差异
    caioj 1172 poj 2823 单调队列过渡题
    数据结构和算法题
    一个通用分页类
  • 原文地址:https://www.cnblogs.com/iOS-Blog/p/4745348.html
Copyright © 2011-2022 走看看