美文网首页Swiftswift入门与提高iOS开发
C++函数指针和Swift的函数对象

C++函数指针和Swift的函数对象

作者: 肆_春分 | 来源:发表于2017-01-20 18:10 被阅读70次

    C++函数指针和Swift的函数对象

    在C++中学习函数指针式非常痛苦的事情,而在Swift里面学习函数指针则是非常愉快的。

    基本语法

    一个函数的声明由函数名,参数列表,返回值三个部分组成,在实际应用中,函数本身也可以当作参数,这个时候就是函数指针,函数指针赋予C语言一定的动态能力,C++的很多的功能都是通过函数指针来实现的,不可谓不重要,在Swift等函数式编程语言中,函数更是一等公民,下面是一些简单的例子

    typedef int (*OPERATOR)(int, int);//使用typedef简化定义,使代码更易懂
    OPERATOR op = max;//不使用typedef的话,可以直接int (*op)(int, int) = max
                      //c++11之后可以auto op = max
    op(10,23);
    
    let op: (Int, Int) -> Int = max
    

    上面C++和Swift定义中op是函数指针的名称,指向具体的函数为max,参数是两个Int,返回值也是Int,看起来非常简单吧。

    类中的函数指针

    请解释一下这段代码:int* (Class::*value[2])(int (*)()),这个value是什么鬼?

    C++中的函数指针需要考虑作用域,比如类成员函数,类静态成员函数,C++的成员函数指针和类本身绑定,类型非常复杂,下面代码中fpr的类型为Number (Number::*)(Number),相比普通的函数指针多了一个作用域

    class Number {
        int x;
    public:
        Number(int value) {
            x = value;
        }
        Number add(Number a){
            return x + a.x;
        }
    };
    Number (Number::*fpr)(Number) = &Number::add;//复杂的类型
    //auto fpr = &Number::add;//c++11
    Number num1(10);
    (num1.*fpr)(20);//成员函数指针的使用方法
    

    在Swift中函数指针的类型就比较简单,Swift中函数和闭包区别不大,都可以使用同样的类型来表示。

    class Number {
        var x:Int
        init(_ v:Int) {
            x = v
        }
        func add(_ b:Number) -> Number {
            return Number(x + b.x)
        }
    }
    var fpr = Number.add
    //fpr的类型是 (Number) -> (Number) -> Number,其中第一个(Number)就是self指针
    let num1 = Number(10)
    let num2 = Number(20)
    let num3 = fpr(num1)(num2)//num1相当于self
    

    在Swift中,所有的函数指针都是一样的,并不区分普通函数指针和成员函数指针,同时,你也没办法在调用的时候感知到这是一个对象成员函数的调用。

    构造函数和析构函数

    C++标准规定不能对构造函数和析构函数取地址,另外我们也知道这两种函数没有返回值,也没办法定义类型,只能是作为特殊函数对待,网上也有一些hack的方法,通过汇编取这两种函数的地址,和我们讨论的函数指针关系不大,这里就不多讨论了。

    在C++里面如果需要构造函数的函数指针,一般会定义一个静态成员函数返回一个对象来完成,比如Number Number::Create(int value)这样的,这样的函数指针也常常出现在工厂方法里面替代直接调用构造函数。

    Swift里面构造函数相比其他函数没有什么特殊的地方,记住两步构造和安全检查就差不多了,毕竟创造是比较复杂的事情,所以这里可以直接取构造函数的指针,比如Number.init就是一个类型为(int) -> Number的函数指针,不过Swift的析构函数因为不能直接调用,所以也不能取地址。

    上下文

    在C++中函数指针是单纯的不带上下文的,在Swift里面不太一样,比如说,在Swift语言中可以对对象的某个函数取指针,接着上面的代码:

    var fpr1 = num1.add
    let num4 = fpr1(num2)
    

    这里的fpr1的上下文中就包含了num1,好比是在闭包捕获了这个对象,调用fpr1和num1.add是等价的,根据这个特性,可以简化很多操作,假如我们有一个int数组,需要转换成Number数组,用map就可以了,代码是这样的:

    let arr2 = arr1.map(Number.init)
    //或者
    let arr2 = arr1.map {Number($0)}`
    

    然后我们要给所有的元素都加上num2这个对象,这里明显的一点是num1.add(num2)和num2.add(num1)是没有区别的,满足交换律的,所以这个动作可以这么实现:

    arr2.map(num2.add)
    //或者
    arr2.map {$0.add(num2)}
    

    是不是也可以认为num1.add是函数(Number) -> (Number) -> Number的curry,num1是第一个参数,结果是(Number) -> Number.

    重载

    overload是现代语言中很重要的一个特性,可以给同一个函数名声明不同的参数,C++中要求参数的个数或类型必须有差异,而Swift中允许相同的参数列表和不同的返回值,根据返回值推断具体的函数调用。前面C++代码里面又一个C++11之后的特性,就是auto类型,编译器自动给你生成类型,免得再去面对鬼一样的函数指针声明,不过这个也不是万能的,一旦遇到重载酒不能工作了,还有就是使用函数指针作参数的时候,肯定还是要一丝不苟的把声明给写出来。那么Swift怎么办呢?默认情况下我们使用let和var的声明变量的时候可以不指定类型,交给编译器来推断,在这种无法推断的地方,编译器也无能为力,这里可以使用明确的类型声明来避免二义性,代码:

    func magicNumber() -> Int {return 2017}
    func magicNumber() -> Float {return 2.20}
    let f: () -> Float = magicNumber
    f()//顺利得到2.20
    

    总结

    Swift在设计的时候有很多函数式编程语言的思想,充分的利用这个特性会让我们的编码效率更高。

    C++的函数指针相对比较复杂,而且经常和数组指针等一起混用变得可读性比较差,需要我们去拆分代码。

    int* (Class::*value[2])(int (*)())这个搞明白了吗?

    typedef Paratype = int (*)();
    typedef int* (Class::*Cptr)(ParaType);
    Cptr value[2];//这就是你要的value,原来是一个数组
                  //数组元素类型是Class类的成员函数指针
                  //函数指针的返回值是int*
                  //函数的参数类型是Paratype也是一个函数指针,是没有参数返回int的函数指针
    

    就这样。

    相关文章

      网友评论

        本文标题:C++函数指针和Swift的函数对象

        本文链接:https://www.haomeiwen.com/subject/pxqgbttx.html