通过coco2dx学习c++之function和lambda

在controller.cpp中有这样一块代码:

typedef struct _Controller{
const char *test_name;
std::function<TestScene*()> callback;
} Controller;
Controller g_aTestNames[] = {
    //
    // TESTS MUST BE ORDERED ALPHABETICALLY
    //     violators will be prosecuted
    //
{ "Accelerometer", []() { return new AccelerometerTestScene(); } },
{ "ActionManager", [](){return new ActionManagerTestScene(); } },
{ "Actions - Basic", [](){ return new ActionsTestScene(); } },
{ "Actions - Ease", [](){return new ActionsEaseTestScene();} },
{ "Actions - Progress", [](){return new ProgressActionsTestScene(); } },
{ "Audio - CocosDenshion", []() { return new CocosDenshionTestScene(); } },
{ "Box2d - Basic", []() { return new Box2DTestScene(); } },
{ "Box2d - TestBed", []() { return new Box2dTestBedScene(); } },
{ "Bugs", []() { return new BugsTestScene(); } },
{ "Chipmunk", []() { return new ChipmunkAccelTouchTestScene(); } },
{ "Click and Move", [](){return new ClickAndMoveTestScene(); } },
{ "Configuration", []() { return new ConfigurationTestScene(); } },
#if (CC_TARGET_PLATFORM != CC_PLATFORM_WINRT)
{ "Console", []() { return new ConsoleTestScene(); } },
#endif
#if (CC_TARGET_PLATFORM != CC_PLATFORM_WINRT)
#if (CC_TARGET_PLATFORM != CC_PLATFORM_EMSCRIPTEN)
#if (CC_TARGET_PLATFORM != CC_PLATFORM_NACL)
#if (CC_TARGET_PLATFORM != CC_PLATFORM_MARMALADE)
#if (CC_TARGET_PLATFORM != CC_PLATFORM_BADA)
{ "Curl", []() { return new CurlTestScene(); } },
#endif
#endif
#endif
#endif
#endif
{ "Current Language", []() { return new CurrentLanguageTestScene(); } },
    { "EventDispatcher", []() { return new EventDispatcherTestScene(); } },
{ "Effects - Advanced", []() { return new EffectAdvanceScene(); } },
{ "Effects - Basic", [](){return new EffectTestScene();} },
{ "Extensions", []() { return new ExtensionsTestScene(); } },
{ "FileUtils", []() { return new FileUtilsTestScene(); } },
{ "Fonts", []() { return new FontTestScene(); } },
{ "Interval", [](){return new IntervalTestScene(); } },
{ "Keyboard", []() { return new KeyboardTestScene(); } },
{ "Keypad", []() { return new KeypadTestScene(); } },
{ "Node: Clipping", []() { return new ClippingNodeTestScene(); } },
{ "Node: Draw", [](){return new DrawPrimitivesTestScene();} },
    { "Node: Label - New API", [](){return new AtlasTestSceneNew(); } },
{ "Node: Label - Old API", [](){return new AtlasTestScene(); } },
{ "Node: Layer", [](){return new LayerTestScene();} },
{ "Node: Menu", [](){return new MenuTestScene();} },
{ "Node: MotionStreak", [](){return new MotionStreakTestScene();} },
{ "Node: Node", [](){return new CocosNodeTestScene();} },
{ "Node: Parallax", [](){return new ParallaxTestScene(); } },
{ "Node: Particles", [](){return new ParticleTestScene(); } },
{ "Node: Physics", []() { return new PhysicsTestScene(); } },
{ "Node: RenderTexture", [](){return new RenderTextureScene(); } },
{ "Node: Scene", [](){return new SceneTestScene();} },
{ "Node: Spine", []() { return new SpineTestScene(); } },
{ "Node: Sprite", [](){return new SpriteTestScene(); } },
    { "Node: Sprite3D", [](){  return new Sprite3DTestScene(); }},
{ "Node: TileMap", [](){return new TileMapTestScene(); } },
{ "Node: Text Input", [](){return new TextInputTestScene(); } },
    { "Node: UI", [](){  return new UITestScene(); }},
    { "Mouse", []() { return new MouseTestScene(); } },
{ "MutiTouch", []() { return new MutiTouchTestScene(); } },
{ "Performance tests", []() { return new PerformanceTestScene(); } },
    { "Renderer", []() { return new NewRendererTestScene(); } },
    { "ReleasePool", [](){ return new ReleasePoolTestScene(); } },
{ "Rotate World", [](){return new RotateWorldTestScene(); } },
{ "Scheduler", [](){return new SchedulerTestScene(); } },
{ "Shader - Basic", []() { return new ShaderTestScene(); } },
    { "Shader - Sprite", []() { return new ShaderTestScene2(); } },
{ "Texture2D", [](){return new TextureTestScene(); } },
{ "TextureCache", []() { return new TextureCacheTestScene(); } },
{ "TexturePacker Encryption", []() { return new TextureAtlasEncryptionTestScene(); } },
{ "Touches", [](){return new PongScene();} },
{ "Transitions", [](){return new TransitionsTestScene();} },
    { "Unit Test", []() { return new UnitTestScene(); }},
{ "UserDefault", []() { return new UserDefaultTestScene(); } },
{ "Zwoptex", []() { return new ZwoptexTestScene(); } },
};

这里用到了function和lambda表达式

funtion的简介如下:

   function是一组函数对象包装类的模板,实现了一个泛型的回调机制。function与函数指针比较相似,优点在于它允许用户在目标的实现上拥有更大的弹性,即目标既可以是普通函数,也可以是函数对象和类的成员函数,而且可以给函数添加状态。

    声明一个function时,需要给出所包装的函数对象的返回值类型和各个参数的类型。比如,声明一个function,它返回一个bool类型并接受一个int类型和一个float类型的参数,可以像下面这样:

function<bool (int, float)> f;

    下面简要介绍一下function的比较重要的几个接口。

function();

    缺省构造函数,创建一个空的函数对象。如果一个空的function被调用,将会抛出一个类型为bad_function_call的异常。

    

template <typename F> function(F g);

    这个泛型的构造函数接受一个兼容的函数对象,即这样一个函数或函数对象,它的返回类型与被构造的function的返回类型或者一样,或者可以隐式转换, 并且它的参数也要与被构造的function的参数类型或者一样,或者可以隐式转换。注意,也可以使用另外一个function实例来进行构造。这样做, 并且function g为空,则被构造的function也为空。使用空的函数指针和空的成员函数指针也会产生空的function。如果这样做,并且function g为空,则被构造的function也为空。使用空的函数指针和空的成员函数指针也会产生空的function。

 

template <typename F> function(reference_wrapper<F> g);

    这个构造函数与前一个类似,但它接受的函数对象包装在一个reference_wrapper中,用以避免通过值来传递而产生函数或函数对象的一份拷贝。这同样要求函数对象兼容于function的签名。

 

function& operator=(const function& g);

    赋值操作符保存g中的函数或函数对象的一份拷贝;如果g为空,被赋值的函数也将为空。

 

template<typename F> function& operator=(F g);

    这个泛型赋值操作符接受一个兼容的函数指针或函数对象。注意,也可以用另一个 function 实例(带有不同但兼容的签名)来赋值。这同样意味着,如果g是另一个function实例且为空,则赋值后的函数也为空。赋值一个空的函数指针或空的成员 函数指针也会使function为空。

 

bool empty() const;

    这个成员函数返回一个布尔值,表示该function是否含有一个函数或函数对象。如果有一个目标函数或函数对象可被调用,它返回 false 。因为一个function可以在一个布尔上下文中测试,或者与0进行比较,因此这个成员函数可能会在未来版本的库中被取消,你应该避免使用它。

 

void clear();

    这个成员函数清除 function, 即它不再关联到一个函数或函数对象。如果function已经是空的,这个调用没有影响。在调用后,function肯定为空。令一个function为空的首选方法是赋0给它;clear 可能在未来版本的库中被取消。

 

result_type operator()(Arg1 a1, Arg2 a2, ..., ArgN aN) const;

    调用操作符是调用function的方法。你不能调用一个空的 function ,那样会抛出一个bad_function_call的异常。调用操作符的执行会调用function中的函数或函数对象,并返回它的结果。

    下面分别给出使用function来包装普通函数,函数对象和类的成员函数的参考代码。

    1、普通函数

1 int Add(int x, int y)2 3 {4             return x+y;5 }6 function<int (int,int)> f = Add;7 int z = f(2, 3);

    2、函数对象

 1 class CStudent 2 { 3 public: 4             void operator() (string strName, int nAge) 5             { 6                 cout << strName << " : " << nAge << endl; 
 7             } 8 }; 9 10 CStudent stu;11 function<void (string, int)> f = stu;12 f("Mike",  12);

    3、类的成员函数

 1 struct TAdd 2 { 3     int Add(int x,int y) 4     { 5         return x+y; 6     } 7 }; 8  9 function<int  (TAdd *, int, int)> f = TAdd::Add;10 TAdd tAdd;11 f(&tAdd, 2, 3);   // 如果前面的模板参数为传值或引用,直接传入tAdd即可

     接下来我们来看看使用function来保存函数对象状态的情况。考虑下面的代码:

 1 class CAdd 2 { 3 public: 4     CAdd():m_nSum(0) { NULL; } 5     int operator()(int i) 6     { 7           m_nSum += i; 8           return m_nSum; 9     }10 11     int Sum() const 12     {13         return m_nSum;14     }15 16 private:17     int m_nSum;18 };19 20 int main() 
21 {22     CAdd add;23     function<int (int)> f1 = add;24     function<int (int)> f2 = add;25     cout << f1(10) << "," << f2(10) << "," << add.Sum() << endl;26     return 0;27 }

     可能和大家想象的结果不一样,上面程序的输出是:10,10,0。我们将同一个函数对象赋值给了两个function,然后分别调用了这两个 function,但函数对象中m_nSum的状态并没有被保持,问题出在哪儿呢?这是因为function的缺省行为是拷贝一份传递给它的函数对象,于 是f1和f2中保存的都是add对象的拷贝,调用f1和f2后,add对象中的值并没有被修改。

    C++ 11中提供了ref和cref函数,来提供对象的引用和常引用的包装。要使function能够正确地保存函数对象的状态,我们可以这样来修改代码:

1 CAdd add;2 function<int(int)> f1 = ref(add);3 function<int(int)> f2 = ref(add);

     另外,在两个function之间赋值时,如果源function保存的是函数对象的拷贝,则目标function保存的也是函数对象的拷贝;如果源function保存的是函数对象的引用,则目标function保存的也是函数对象的引用

C++11 lambda表达式

lambda本质上就是一个函数指针。

C++11引入了lambda表达式,使得程序员可以定义匿名函数,该函数是一次性执行的,既方便了编程,又能防止别人的访问。

Lambda表达式的语法通过下图来介绍:

 

通过coco2dx学习c++之function和lambda_第1张图片

         这里假设我们定义了一个如上图的lambda表达式。现在来介绍途中标有编号的各个部分是什么意思。

  1. Lambda表达式的引入标志,在‘[]’里面可以填入‘=’或‘&’表示该lambda表达式“捕获”(lambda表达式在一定的 scope可以访问的数据)的数据时以什么方式捕获的,‘&’表示一引用的方式;‘=’表明以值传递的方式捕获,除非专门指出。

  2. Lambda表达式的参数列表

  3. Mutable 标识

  4. 异常标识

  5. 返回值

  6. “函数”体,也就是lambda表达式需要进行的实际操作

 

将上图的代码片段补充完整:

    int x = 10;

    int y = 3;

    int z ;

    z = [=]()mutable throw() -> int { int n = x + y; x = y ; y = n; return n;}();

    cout<<z<<endl;

    cout<<"x:"<<x<<"\t"<<"y:"<<y<<endl;

运行结果为:

13

x: 10  y: 3

因为是以值传递的方式访问x,y所以x,y的值并没有发生改变

 

现在我们队lambda表达式的基本语法已经有一些了解,下面来举几个例子。

 

 

首先这个例子说明如何向lambda表达式里面传递参数:

#include <iostream>

using namespace std;

int main()

{

   int n = [] (int x, int y) { return x + y; }(5, 4);

   cout << n << endl;

}

运行结果为:9

通过这个例子我们可以看出,通过“函数体”后面的‘()’传入参数。

 

接下来这个例子可以看出,可以像调用函数一样使用lambda表达式,但是感觉这种方式和普通函数的定义与调用就差不多了,这里只是学习使用方式而已。

#include <iostream>

using namespace std;

int main()

{

 

   auto f = [] (int x, int y) { return x + y; };

 

   cout << f(21, 12) << endl;

}

运行结果为:33

 

Lambda表达式与STL算法一起使用,自己写测试代码的时候经常用到排序、输出数组什么的,通过下面列举的几个算法也比较方便:

#include <iostream>

#include <algorithm>

#include <ctime>

using namespace std;

 

int main()

{

    int a[10] = {0};

 

    srand(time(NULL));

    generate(a,a+10,[]()->int { return rand() % 100; });

 

    cout<<"before sort: "<<endl;

        for_each(a, a+10, [&](int i){ cout<< i <<" "; });

 

    cout<<endl;

    cout<<"After sort"<<endl;

    sort(a,a+10);

    for_each(a, a+10, [&](int i){ cout<< i <<" "; });

    return 0;

}

 

Lambda表达式的嵌套:

#include <iostream>

int main()

{

   using namespace std;

 

   int m = [](int x)

      { return [](int y) { return y * 2; }(x) + 3; }(5);

 

   cout << m << endl;

}

运行结果:13

 

以上代码在VC10和VC11上都能顺利编译通过。感觉lambda表达式还是比较有意思的语法,也是我接触的第一个VC11扩展。


你可能感兴趣的:(通过coco2dx学习c++之function和lambda)