软件中的一些特殊机制
1.回调函数
2.多态
3.闭包
4.异步调用
5.匿名函数
一,回调函数
关于回调函数的概念,上图可以很清晰的描述:应用程序在调用库函数时向库函数传递参数,参数里面包含一个应用程序指定的函数。这样,库函数被调用的时候,就会回过头来调用这个应用程序指定的函数。被传递而后被库函数调用的函数称为回调函数(callback function)。
这么看来,回调函数会大大增加编程的灵活性。因为相比传统仅传递参数的方式,这里把函数作为参数,库函数的动作可以根据回调函数的不同而做出相应的改变。
举个简单的例子,如下:
// callBack1:接受一个非零整数,返回偶数
int cb_Even(int a){
return 2*a;
}
// callBack2:接受一个非零整数,返回奇数
int cb_Odd(int a){
return 2*a+1;
}
// 中间函数:接受函数指针并调用对应的回调函数
int returnNum(int a, int (*p_func)(int)){
return (*p_func)(a);
}
// 使用:传入不同的函数参数,调用不同的回调函数
cout << returnNum(5, cb_Even) << endl; // 输出10
cout << returnNum(5, cb_Odd) << endl; // 输出11
我对回调函数的理解:发起者将目标函数A作为参数传递给中间函数,中间函数随后调用目标函数A。相比仅传递参数,将函数也传递过去可以执行不同的功能。
二,多态
在面向对象编程中,多态是指通过基类的指针或引用,在运行时,根据实际绑定的对象执行对应的函数的行为。编译时绑定则是指重载或模板。关于多态的实现原理可以查阅虚函数表相关内容。
c++中,实现多态的条件:
-
要有继承关系;
-
要有虚函数重写(被 virtual 声明的函数叫虚函数);
-
要有父类指针(父类引用)指向子类对象。
举个简答的例子,如下:
class Person{
public:
// 父类中的虚函数
virtual void BuyTicket(){
cout << "adult need full fare." << endl;
}
}
class Child : public Person{
public:
// 子类继承并重写了父类的虚函数
virtual void BuyTicket(){
cout << "child free." << endl;
}
}
void TicketCheck(Person& person){
// 编译时,父类指针指向自己的虚函数
person.BuyTicket();
}
// 调用时,该指针动态指向自己或任意孩子重写的虚函数
Person father; Child tommy, Child bobby;
TicketCheck(father); // person指针指向person
TicketCheck(tommy); // person指针指向child
TicketCheck(bobby); // person指针指向child
三,闭包
闭包(closure)是JavaScript中的概念,指函数与函数内部能访问到的外部变量构成的总和。
比如:bar()函数和其内部能够访问到的外部变量local的组合,即为一个闭包。注意这里有一个刻意为之的函数嵌套的结构。
function foo(){
var local = 1 // 闭包的作用就是将local变量变为局部变量
function bar(){ // 函数嵌套,local称为bar函数的全局变量
locat++
return local
}
return bar // 返回bar函数,外部可以间接使用local变量
}
闭包常用来隐藏一个变量,将其变为局部变量,外界通过间接访问的方式来使用这个变量。
四,异步调用
首先明确一下同步调用的概念:当发起者A调用函数B时,会一直处于阻塞状态直到函数B执行完并返回结果。异步调用则是指发起者A调用函数B后继续做其他的事情,不会等待B的执行。
原理比较简单,在C++中可以通过调用函数async()来实现异步调用。
五,匿名函数
匿名函数,顾名思义,就是没有名称的函数,在C++和大多数语言中又称为lambda表达式。
下图为lambda表达式的书写规范,这里讨论第二种模式。
[捕获列表](参数列表) -> 返回值类型 { 函数题 }(传递的参数列表);
举个例子,看一下普通函数和匿名函数(lambda表达式)的区别:
// 普通函数:接受2个整型值返回和
int sum(int a, int b){
return a+b;
}
int main(){
int outcome=0; // 变量用于接受结果
// 调用普通函数
outcome = sum(1, 2);
cout << outcome << endl;
// 调用lambda表达式,注意,lambda表达式可以在函数内部嵌套定义,这是优势
outcome = [](int a, int b) -> int { return a+b; }(1, 2);
cout << outcome << endl;
// 或者可以写成这样
[](int a, int b){ cout << a+b << endl; }(1, 2);
return 0;
}
一般而言在C++中,无法在main函数或者其他函数内部定义新的函数,这会发生函数嵌套。从上面的例子可以看出,使用lambda表达式可以随用随定义,不用跳出函数内部。另一方面,不用给函数取名,也省了一些命名的烦恼 :)。
关于lambda表达式的进阶用法这里不再赘述,请自行查阅相关资料。