C++函數(shù)指針和Swift的函數(shù)對(duì)象
在C++中學(xué)習(xí)函數(shù)指針式非常痛苦的事情,而在Swift里面學(xué)習(xí)函數(shù)指針則是非常愉快的京髓。
基本語法
一個(gè)函數(shù)的聲明由函數(shù)名弯汰,參數(shù)列表综慎,返回值三個(gè)部分組成莱找,在實(shí)際應(yīng)用中酬姆,函數(shù)本身也可以當(dāng)作參數(shù),這個(gè)時(shí)候就是函數(shù)指針奥溺,函數(shù)指針賦予C語言一定的動(dòng)態(tài)能力辞色,C++的很多的功能都是通過函數(shù)指針來實(shí)現(xiàn)的,不可謂不重要浮定,在Swift等函數(shù)式編程語言中相满,函數(shù)更是一等公民,下面是一些簡(jiǎn)單的例子
typedef int (*OPERATOR)(int, int);//使用typedef簡(jiǎn)化定義桦卒,使代碼更易懂
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是函數(shù)指針的名稱,指向具體的函數(shù)為max方灾,參數(shù)是兩個(gè)Int建蹄,返回值也是Int,看起來非常簡(jiǎn)單吧裕偿。
類中的函數(shù)指針
請(qǐng)解釋一下這段代碼:int* (Class::*value[2])(int (*)())
洞慎,這個(gè)value是什么鬼?
C++中的函數(shù)指針需要考慮作用域嘿棘,比如類成員函數(shù)劲腿,類靜態(tài)成員函數(shù),C++的成員函數(shù)指針和類本身綁定鸟妙,類型非常復(fù)雜焦人,下面代碼中fpr的類型為Number (Number::*)(Number)
,相比普通的函數(shù)指針多了一個(gè)作用域
class Number {
int x;
public:
Number(int value) {
x = value;
}
Number add(Number a){
return x + a.x;
}
};
Number (Number::*fpr)(Number) = &Number::add;//復(fù)雜的類型
//auto fpr = &Number::add;//c++11
Number num1(10);
(num1.*fpr)(20);//成員函數(shù)指針的使用方法
在Swift中函數(shù)指針的類型就比較簡(jiǎn)單圆仔,Swift中函數(shù)和閉包區(qū)別不大垃瞧,都可以使用同樣的類型來表示。
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坪郭,其中第一個(gè)(Number)就是self指針
let num1 = Number(10)
let num2 = Number(20)
let num3 = fpr(num1)(num2)//num1相當(dāng)于self
在Swift中个从,所有的函數(shù)指針都是一樣的,并不區(qū)分普通函數(shù)指針和成員函數(shù)指針歪沃,同時(shí)嗦锐,你也沒辦法在調(diào)用的時(shí)候感知到這是一個(gè)對(duì)象成員函數(shù)的調(diào)用。
構(gòu)造函數(shù)和析構(gòu)函數(shù)
C++標(biāo)準(zhǔn)規(guī)定不能對(duì)構(gòu)造函數(shù)和析構(gòu)函數(shù)取地址沪曙,另外我們也知道這兩種函數(shù)沒有返回值奕污,也沒辦法定義類型,只能是作為特殊函數(shù)對(duì)待液走,網(wǎng)上也有一些hack的方法碳默,通過匯編取這兩種函數(shù)的地址贾陷,和我們討論的函數(shù)指針關(guān)系不大,這里就不多討論了嘱根。
在C++里面如果需要構(gòu)造函數(shù)的函數(shù)指針髓废,一般會(huì)定義一個(gè)靜態(tài)成員函數(shù)返回一個(gè)對(duì)象來完成,比如Number Number::Create(int value)
這樣的该抒,這樣的函數(shù)指針也常常出現(xiàn)在工廠方法里面替代直接調(diào)用構(gòu)造函數(shù)慌洪。
Swift里面構(gòu)造函數(shù)相比其他函數(shù)沒有什么特殊的地方,記住兩步構(gòu)造和安全檢查就差不多了凑保,畢竟創(chuàng)造是比較復(fù)雜的事情冈爹,所以這里可以直接取構(gòu)造函數(shù)的指針,比如Number.init
就是一個(gè)類型為(int) -> Number
的函數(shù)指針欧引,不過Swift的析構(gòu)函數(shù)因?yàn)椴荒苤苯诱{(diào)用频伤,所以也不能取地址。
上下文
在C++中函數(shù)指針是單純的不帶上下文的维咸,在Swift里面不太一樣剂买,比如說,在Swift語言中可以對(duì)對(duì)象的某個(gè)函數(shù)取指針癌蓖,接著上面的代碼:
var fpr1 = num1.add
let num4 = fpr1(num2)
這里的fpr1的上下文中就包含了num1瞬哼,好比是在閉包捕獲了這個(gè)對(duì)象,調(diào)用fpr1和num1.add是等價(jià)的租副,根據(jù)這個(gè)特性坐慰,可以簡(jiǎn)化很多操作,假如我們有一個(gè)int數(shù)組用僧,需要轉(zhuǎn)換成Number數(shù)組结胀,用map就可以了,代碼是這樣的:
let arr2 = arr1.map(Number.init)
//或者
let arr2 = arr1.map {Number($0)}`
然后我們要給所有的元素都加上num2這個(gè)對(duì)象责循,這里明顯的一點(diǎn)是num1.add(num2)和num2.add(num1)是沒有區(qū)別的糟港,滿足交換律的,所以這個(gè)動(dòng)作可以這么實(shí)現(xiàn):
arr2.map(num2.add)
//或者
arr2.map {$0.add(num2)}
是不是也可以認(rèn)為num1.add是函數(shù)(Number) -> (Number) -> Number的curry院仿,num1是第一個(gè)參數(shù)秸抚,結(jié)果是(Number) -> Number.
重載
overload是現(xiàn)代語言中很重要的一個(gè)特性,可以給同一個(gè)函數(shù)名聲明不同的參數(shù)歹垫,C++中要求參數(shù)的個(gè)數(shù)或類型必須有差異阴挣,而Swift中允許相同的參數(shù)列表和不同的返回值臼寄,根據(jù)返回值推斷具體的函數(shù)調(diào)用棉圈。前面C++代碼里面又一個(gè)C++11之后的特性幌氮,就是auto類型,編譯器自動(dòng)給你生成類型暮芭,免得再去面對(duì)鬼一樣的函數(shù)指針聲明鹿驼,不過這個(gè)也不是萬能的欲低,一旦遇到重載酒不能工作了,還有就是使用函數(shù)指針作參數(shù)的時(shí)候蠢沿,肯定還是要一絲不茍的把聲明給寫出來伸头。那么Swift怎么辦呢匾效?默認(rèn)情況下我們使用let和var的聲明變量的時(shí)候可以不指定類型舷蟀,交給編譯器來推斷,在這種無法推斷的地方面哼,編譯器也無能為力野宜,這里可以使用明確的類型聲明來避免二義性,代碼:
func magicNumber() -> Int {return 2017}
func magicNumber() -> Float {return 2.20}
let f: () -> Float = magicNumber
f()//順利得到2.20
總結(jié)
Swift在設(shè)計(jì)的時(shí)候有很多函數(shù)式編程語言的思想魔策,充分的利用這個(gè)特性會(huì)讓我們的編碼效率更高匈子。
C++的函數(shù)指針相對(duì)比較復(fù)雜,而且經(jīng)常和數(shù)組指針等一起混用變得可讀性比較差闯袒,需要我們?nèi)ゲ鸱执a虎敦。
int* (Class::*value[2])(int (*)())
這個(gè)搞明白了嗎?
typedef Paratype = int (*)();
typedef int* (Class::*Cptr)(ParaType);
Cptr value[2];//這就是你要的value政敢,原來是一個(gè)數(shù)組
//數(shù)組元素類型是Class類的成員函數(shù)指針
//函數(shù)指針的返回值是int*
//函數(shù)的參數(shù)類型是Paratype也是一個(gè)函數(shù)指針其徙,是沒有參數(shù)返回int的函數(shù)指針
就這樣。