`

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

 
阅读更多

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

作者:阳光下的蒲公英

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

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

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

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

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

1     auto closeItem = MenuItemImage::create(2                                            "CloseNormal.png",3                                            "CloseSelected.png",4                                            CC_CALLBACK_1(HelloWorld::menuCloseCallback, this));
 

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

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

<!--[if gte vml 1]><v:shapetype id="_x0000_t75" coordsize="21600,21600" o:spt="75" o:preferrelative="t" path="m@4@5l@4@11@9@11@9@5xe" filled="f" stroked="f"> <v:stroke joinstyle="miter"/> <v:formulas> <v:f eqn="if lineDrawn pixelLineWidth 0"/> <v:f eqn="sum @0 1 0"/> <v:f eqn="sum 0 0 @1"/> <v:f eqn="prod @2 1 2"/> <v:f eqn="prod @3 21600 pixelWidth"/> <v:f eqn="prod @3 21600 pixelHeight"/> <v:f eqn="sum @0 0 1"/> <v:f eqn="prod @6 1 2"/> <v:f eqn="prod @7 21600 pixelWidth"/> <v:f eqn="sum @8 21600 0"/> <v:f eqn="prod @7 21600 pixelHeight"/> <v:f eqn="sum @10 21600 0"/> </v:formulas> <v:path o:extrusionok="f" gradientshapeok="t" o:connecttype="rect"/> <o:lock v:ext="edit" aspectratio="t"/> </v:shapetype><v:shape id="_x0000_i1025" type="#_x0000_t75" style='width:15pt; height:15pt'> <v:imagedata src="file:///C:\Users\guanjh\AppData\Local\Temp\msohtmlclip1\01\clip_image001.png" o:href="http://common.cnblogs.com/images/copycode.gif"/> </v:shape><![endif]--><!--[if !vml]--><!--[endif]-->

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,onTouchesBeganstd::function来定义的。那么std::bindstd::function又有什么区别呢?

有博文说:

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

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

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

std::bind绑定器

<!--[if !supportLists]-->·         <!--[endif]-->将函数、成员函数和闭包转成function函数对象

<!--[if !supportLists]-->·         <!--[endif]-->将多元(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需要注意的一些事项:

<!--[if !supportLists]-->·         <!--[endif]-->1bind预先绑定的参数需要传具体的变量或值进去,对于预先绑定的参数,是pass-by-value

<!--[if !supportLists]-->·         <!--[endif]-->2)对于不事先绑定的参数,需要传std::placeholders进去,从_1开始,依次递增。placeholderpass-by-reference

<!--[if !supportLists]-->·         <!--[endif]-->3bind的返回值是可调用实体,可以直接赋给std::function对象

<!--[if !supportLists]-->·         <!--[endif]-->4)对于绑定的指针、引用类型的参数,使用者需要保证在可调用实体调用之前,这些参数是可用的

<!--[if !supportLists]-->·         <!--[endif]-->5)类的this可以通过对象或者指针来绑定

std::function

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

<!--[if !supportLists]-->·         <!--[endif]-->函数和成员函数作为function

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

 1 typedef std::function<float (int x, int y)> f ;// 构造一个函数对象,它能表示的是一个返回值为float,两个参数为intint的函数   2struct int_div {        // 构造一个可以使用"()"进行调用的函数对象类型   3     floatoperator() (int x, int y) const { return ((float)x)/y; }; 4 }; 5 6void 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:vs2012bug给坑了。因为看网上的代码于是刚开始第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才能编译。

<!--[if !supportLists]-->·         <!--[endif]-->可以用function取代函数指针。因为它可以保存函数延迟执行,所以比较适合作为回调函数,也可以把它看做类似于c#中特殊的委托,只有一个成员的委托。

 1struct int_div {        // 构造一个可以使用"()"进行调用的函数对象类型   2     floatoperator() (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 };

<!--[if !supportLists]-->·         <!--[endif]-->function还可以作为函数入参,这样可以在函数外部控制函数的内部行为了,让我们的函数变得更加灵活。

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是为了替换和增强之前标准库的bind1stbind2st,让我们的用起来更方便!

 

 

更多精品iOSCocos、移动设计课程请关注智捷课堂官方网站:<!--[if gte vml 1]><v:shape id="图片_x0020_1" o:spid="_x0000_i1029" type="#_x0000_t75" style='width:15pt;height:11.25pt; visibility:visible;mso-wrap-style:square'> <v:imagedata src="file:///C:\Users\guanjh\AppData\Local\Temp\msohtmlclip1\01\clip_image002.png" o:title="%W@GJ$ACOF(TYDYECOKVDYB"/> </v:shape><![endif]--><!--[if !vml]--><!--[endif]-->http://www.zhijieketang.com

 

 

 

 

 

 

分享到:
评论

相关推荐

    cocos2d-x 3.0

    《cocos2d-x 3.0:游戏开发中的角色移动技术详解》 在游戏开发领域,cocos2d-x是一款广泛使用的开源2D游戏引擎,尤其在移动平台上的应用非常广泛。cocos2d-x 3.0版本带来了许多性能优化和新特性,使得开发者能够更...

    Cocos2d-x 3.0的俄罗斯方块代码

    《Cocos2d-x 3.0的俄罗斯方块代码》是一个基于Cocos2d-x游戏引擎3.0及以上版本实现的经典游戏项目。Cocos2d-x是一个广泛使用的开源跨平台2D游戏开发框架,它允许开发者用C++、Lua或JavaScript编写游戏,并在iOS、...

    Cocos2d-x3.0正式版 练练看 源码

    10. **游戏交互**:文件名“20140823_01_llk_游戏交互.wrf”可能包含一个关于游戏交互的示例或教程,展示了如何在Cocos2d-x3.0中实现玩家与游戏的互动,例如角色控制、菜单操作、游戏事件响应等。 总的来说,Cocos...

    cocos2d-x-3.0 类图

    这是我重新弄的cocos2d-x-3.0的类图.之前别人兄台弄的,有些不全面,有些地方错误.我这个可以说是最新的了.每个类添加了中文的详细注解,同时也添加了中文的类名称翻译.这样对cocos2d-x-3.0的框架比较好上手. 有兴趣的...

    Cocos2d-x实战:C++卷(2版)源代码

    《Cocos2d-x实战:C++卷(2版)源代码》这本书是关于使用Cocos2d-x游戏引擎进行游戏开发的专业指南。Cocos2d-x是一个开源的、跨平台的游戏开发框架,广泛应用于iOS、Android、Windows等多个操作系统。本书以C++语言...

    Cocos2d-x实战:JS卷——Cocos2d-JS开发

    资源名称:Cocos2d-x实战:JS卷——Cocos2d-JS开发内容简介:本书是介绍Cocos2d-x游戏编程和开发技术书籍,介绍了使用Cocos2d-JS中核心类、瓦片地图、物理引擎、音乐音效、数据持久化、网络通信、性能优化、多平台...

    cocos2d-x3.0rc0 中文API

    《cocos2d-x 3.0中文API详解》 cocos2d-x是一个开源的、跨平台的游戏开发框架,广泛应用于2D游戏、实验性的3D游戏以及各种交互式应用的开发。3.0版本的发布带来了许多新特性和改进,其中中文API文档的推出更是为...

    cocos2d-x 3.0-alpha0 Lib文件

    3.0-alpha0是cocos2d-x的一个重要版本,它引入了诸多新特性与优化,旨在提升开发效率和游戏性能。本篇文章将详细介绍cocos2d-x 3.0-alpha0中的Lib文件,以及其在游戏开发中的应用。 首先,Lib文件是cocos2d-x框架的...

    SneakyJoystick-Cocos2d-x 3.0rc0

    《SneakyJoystick-Cocos2d-x 3.0rc0:打造高效游戏控制体验》 Cocos2d-x是一款流行的开源游戏开发框架,它使用C++编写,支持多种平台,包括iOS、Android以及Windows等。在游戏开发中,交互性是至关重要的,而...

    cocos2d-x 3.0创建项目

    下载并解压Cocos2d-x 3.0的压缩包,然后将`[cocos-root]/tools/cocos2d-console/bin/`路径添加到环境变量Path中。这使得你可以从任何目录调用cocos命令行工具。 3. **验证环境**: 打开命令行终端,切换到`cocos...

    animatepacker +cocos2d-x3.0接口封装

    《animatepacker与cocos2d-x3.0接口封装详解》 在游戏开发领域,cocos2d-x是一款广泛使用的2D游戏引擎,而animatepacker则是一款强大的动画处理工具。本文将深入探讨如何将animatepacker的功能与cocos2d-x3.0进行...

    cocos2d-x3.0导出自定义类到lua的测试源文件

    《cocos2d-x3.0中自定义类与Lua交互的深度解析》 在cocos2d-x 3.0版本中,开发者们经常需要将C++自定义的类暴露给Lua脚本使用,以便在游戏中实现更复杂的逻辑和交互。这篇文档将详细解释如何将C++自定义类导出到Lua...

    cocos2d-x 3.0 +cocostudio 1.4 奇怪的大冒险

    《cocos2d-x 3.0与cocostudio 1.4:打造奇怪的大冒险》 在游戏开发领域,cocos2d-x 和 cocostudio 是两款强大的工具,尤其适用于初学者。cocos2d-x 是一个开源的2D游戏引擎,广泛应用于iOS、Android以及Windows等多...

    兄弟连cocos2d-x3.0 final视频教程完整版

    ### Cocos2d-x3.0 Final视频教程知识点解析 #### 一、Cocos2d-x简介 Cocos2d-x是一款开源的游戏开发框架,它支持多种编程语言,包括C++、JavaScript和Lua等。该框架特别适用于移动平台游戏的开发,支持iOS、...

    Cocos2d-x3.0游戏实例《别救我》源码

    Cocos2d-x3.0是一款广泛应用于游戏开发的开源框架,主要支持2D游戏的构建,基于C++,同时也提供了Lua和JavaScript的绑定。它以其高效、跨平台的特性,使得开发者能够轻松地在iOS、Android、Windows等多个平台上发布...

    Cocos2d-x 3.0 过渡学习

    Cocos2d-x是一个开源的游戏开发框架,它被广泛应用于iOS、...对于新旧版本之间的不兼容性,开发者需要关注官方文档,了解所有细节变化,并逐步迁移和适配现有项目,以便充分利用Cocos2d-x 3.0带来的新特性和性能提升。

    cocos2d-x 3.0 类关系图

    这是cocos2d-x 3.0 类关系图, 这个制作者是:gamecocos2dx 他做的Xmind,我导出的PNG格式,我推荐还是看Xmind,里面还有一些注释 gamecocos2dx Xmind下载地址:...

    cocos2d-x实战 c++卷教程及完整源码

    cocos2d-x实战 c++卷教程及完整源码下载,使用最新cocos2d-x-3.14版本,在xcode7.3上已编译通过。 解决相关问题 1、解决源程序在高版本上无法编译问题 2、解决源程序中文注释部分,xcode上显示乱码问题 3、根据书籍...

    cocos2d-x 3.0 骨骼动画 demo

    《cocos2d-x 3.0 骨骼动画技术详解及应用实践》 cocos2d-x 是一个广泛使用的开源游戏开发框架,而3.0版本在原有的基础上引入了骨骼动画功能,大大增强了2D游戏的角色表现力和动态效果。本篇文章将围绕“cocos2d-x ...

    cocos2d-x 3.0 离线文档

    cocos2d-x是一个开源的游戏开发框架,基于C++,并支持多平台,包括iOS、Android、Windows以及Mac OS等。它为游戏开发提供了丰富的功能,如图形渲染、动画处理、物理引擎、音频管理以及用户界面设计等。 离线文档...

Global site tag (gtag.js) - Google Analytics