自从 C++标准委员会发布 C++11 标准后,引进了一些新的知识点,如 auto 指针、Lambda表达式、bind和function函数等,Cocos2d-x引擎把这些知识点也加入了引擎的编写过程中。
在 C++中,有许多算法用来对比输入序列,如 find_if、sort、for_each 等,这些算法第三个参数断言(predicate)既可以是“<;”或“==”操作符,也可以是一个能调用的对象(函数、函数指针等)。这些断言普遍的一个特点是,要么带一个参数,要么带两个参数。例如,find_if算法中的断言是带一个参数的,如果要在一个vector容器中查找第一为奇数的值时,其实现程序如程序片段1-5所示。
程序片段1-5 find_if用法
代码中阐述了 find_if 函数的用法,参数中的断言是带一个参数的。而 sort 算法中的断言,它的实现带两个参数,如程序片段1-6的例子。
程序片段1-6 sort用法
有时,我们想传递多于断言参数个数的时候,如在find_if算法中,要求在list容器中查找第一个奇数,且它的值大于3时,IsOdd断言就无法实现了。
为了解决这类问题,C++中引入了 Lambda 表达式,它是一个可调用对象,可以认为它是一个未命名的内联函数,有返回值、参数列表和函数体等,其类型如图1-6所示。
图1-6 Lambda表达式原型
Lambda 表达式中除了捕获列表外,其他的三个部分和普通函数类似。其中,参数列表和返回类型可以省略,其他两个必须存在,一个简单的例子如程序片段1-7所示。
程序片段1-7 Lambda表达式例子
这段代码执行后,会在控制台上打印出“Hello World”的字样。如果函数体中只有一个带return的语句,那么表达式可以推断出返回类型,否则将返回void类型。
1.捕获列表
捕获列表捕获的是调用该表达式函数内的局部非静态变量,对于那些函数内的静态变量和定义在函数外的变量,Lambda表达式可以直接使用。捕获列表的类型如表1-5所示。
表1-5 捕获类型
有了捕获列表之后,就可以解决IsOdd断言问题了,实现方式如程序片段1-8所示。
程序片段1-8 Lambda应用
上述Lambda表达式中捕获了odd变量,返回类型是bool。
编译器在解释 Lambda 表达式时,会解释成一个未命名类的未命名对象,该类包含了一个重载函数调用操作符的函数,如果在捕获列表中包含有值捕获参数时,那么在该类中会有一个相对应的成员参数,如上面的 Lambda 表达式中,编译器会解释类似,Lambda 表达式对应的类如程序片段1-9所示。
程序片段1-9 Lambda表达式对应的类
默认情况下,Lambda表达式不会改变其捕获的值,所以函数调用操作符是const函数,如果Lambda表达式是mutable,那么函数调用操作符函数是非const函数。
2.返回值
如果Lambda表达式的函数体不只是一个return语句,那么这时就应该指定表达式的返回值了,它由一个箭头后面带一个返回类型来制定,如程序片段1-8所示,返回类型为bool。
对于 Lambda 表达式,通常只会使用在一个地方,且执行语句较少,如果有多个地方用到该功能,使用函数来实现会更方便些。另一种情况是,如果捕获列表为空,通常使用函数来实现比较直接。
如果一个断言要多于指定的参数,使用函数来实现该功能时,不能把该函数传递给find_if 这样的函数。后来引进了 Lambda 这样的表达式,那能不能直接使用函数来代替Lambda这样的表达式实现同样的功能呢?这就是下一小节将要介绍的库函数bind。
C++提供的库函数 bind,用于函数绑定的模版,它可以指定被绑定函数中的部分参数,也可以是全部参数,其形式如程序片段1-10所示。
程序片段1-10 bind函数
其中:func 是原函数,arg_list 是 func 函数中的参数,是一个逗号分割的参数列表,其中参数可以用占位符来表示,_1 表示 func 函数的第一个参数,_2 表示该函数的第二个参数,以此类推,这些占位符可以交换顺序,它们位于std的placeholders命名空间下,f是一个新的可调用对象。对于bind函数的使用如程序片段1-11所示。
程序片段1-11 bind函数使用
有了bind函数,同样可以解决程序片段1-8中的问题,实现如程序片段1-12所示。
程序片段1-12 bind函数使用
bind函数指定了func函数中的第二个参数,而第一个参数是一个占位符。
默认情况下,bind 函数在不使用占位符时,参数是直接进行复制的。有时,某些绑定函数想通过引用传递或者有的参数是不能复制时,如程序片段1-13所示。
程序片段1-13 带引用参数的函数
在这种情况下,C++引进了ref和cref函数。其中,cref是const引用,使用ref函数解决了os不能复制的问题,使用如程序片段1-14所示。
程序片段1-14 bind函数使用引用参数
C++中的可调用对象,包括函数、函数指针、Lambad 表达式、bind 函数创建的对象和重载了函数调用操作符的类,这些对象可以存储在 function 函数模版中,下面将分别来介绍这些对象的使用。
1.函数
对于函数的使用,如程序片段1-15所示。
程序片段1-15 function绑定函数
2.Lambad表达式
对于Lambad表达式的使用,如程序片段1-16所示。
程序片段1-16 function绑定Lambda表达式
3.bind函数
对于bind函数的使用,如程序片段1-17所示。
程序片段1-17 function绑定bind函数
4.重载了()操作符的类
对于这种类型的类使用,如程序片段1-18所示。
程序片段1-18 function绑定重载了()的类
上面几个程序片段演示了 function 函数部分功能的使用,它们也是 Cocos2d-x 引擎比较常用的功能,读者在实际使用中可以慢慢体会。
C++11 引入了 auto 关键字。由 auto 定义的变量,通过对该变量的初始化来推断它的类型,使用如程序片段1-19所示。
程序片段1-19 auto使用
nullptr是新标准中的一个关键字,表示一个空指针。在早期用NULL宏或0来表示,其中 NULL 宏的定义就是为 0,所以对于数字 0,既可以表示一个整数 0,又可以表示一个指针。对于这个空指针的使用,如程序片段1-20所示。
程序片段1-20 nullptr使用
C++语言中存在虚函数,有时子类要重写父类的一个函数,但同样一个函数可能不小心多加了一个参数,对于这种问题,有时很难发现,如父类有函数virtual int add(int,int),子类原本想重写该函数,但在声明过程中却写成了virtual int add(int,int,int),这样子类的add就没有重写父类的同名函数,而是一个新的函数,如果在子类的声明中写成 int add(int,int,int)override,则程序在编译时就会报错,因为父类根本就没有一个这样的函数。
override关键字表明,子类的函数一定重写了父类函数。而final 关键字表明,该函数不能被继承,已经是最终版本了,如果子类有重写该函数,则编译器就会报错。