一、為什麼要過載運算子
我們都知道所謂的運算子平常的也就是 + - * /
之類的,比如我們隨便寫個 1 + 2
列印肯定輸出的是 3
,那麼我們為什麼還要過載運算子呢?下面我們就舉個例子,如下我定義個結構體
1 2 3 4 5 6 |
struct Vector { var x: Int = 0 var y: Int = 0 var z: Int = 0 } |
然後我們定義兩個變數 V1,V2
1 2 |
var V1 = Vector(x: 1, y: 2, z: 3) var V2 = Vector(x: 4, y: 5, z: 6) |
然後我們來寫V1 + V2
,報錯
報錯 +
不能用於 Vector
,這是因為 Vector
使我們自定義的一個結構體,所以編譯器不知道我們要用 +
對這個結構體做什麼操作。因此,在這裡我們就需要用到運算子的過載。
二、如何過載運算子
- 1、含有兩個引數的運算子的過載
因為運算子是一個函式,比如對於陣列的reduce
方法我們就可以如下
12let arr = [1, 2, 3, 4]arr.reduce(0, +)
結果是 10 ,這裡的+
就代表了一個函式,所以我們重新寫的時候可以如下
1234func + (left: Vector, right: Vector) -> Vector {return Vector(x: left.x + right.x, y: left.y + right.y, z: left.z + right.z)}
這樣我們就實現了+
的過載,上面函式中 left 和 right 兩個引數都是 Vector 型別的,一個表示運算子左邊的引數一個表示運算子右邊的引數(這兩個引數是有先後順序的,由於加法滿足加法交換律所以這裡體現不出來,有興趣的可以試一下-
的過載,這時候就要注意順序了),然後返回值得型別也是 Vector 型別,再來實現V1 + V2
的時候,就發現得到了一個新的 Vector 型別的值
這裡我們就已經完成了 +
這個運算子的過載。當然有興趣的童鞋還可以試著自己實現 -
或者 *
的過載,這裡就不一一舉例了。
關於雙目運算子的過載,和單目運算子類似,如下
1 2 3 4 |
func += (left: inout Vector, right: Vector) { left = left + right } |
執行結果
- 2、含有一個引數的運算子的過載
照著上面單目運算子的方式我們自己來寫個-
過載例子,如下
1234func - (vector: Vector) -> Vector {return Vector(x: -vector.x, y: -vector.y, z: -vector.z)}
按照我們的邏輯這裡取反邏輯上應該是正確的,可是編譯會發現報錯
這裡我們就要注意了,和有兩個引數的運算子不同的是,只有一個引數的運算子位置是不固定的,這裡的 -
可以在前可以在後,所以我們在這裡還需要注意運算子的位置
1 2 3 4 |
prefix func - (vector: Vector) -> Vector { return Vector(x: -vector.x, y: -vector.y, z: -vector.z) } |
這裡加上一個 prefix 表示前置(後置是 postfix)。這樣就可以明確運算子的位置
- 3 、比較運算子的過載
關於比較運算子的過載,顧名思義也是有兩個引數的,返回值肯定是個Bool
型別的,如下過載==
運算子
1 2 3 4 |
func == (left: Vector, right: Vector) -> Bool { return left.x == right.x && left.y == right.y && left.z == right.z } |
再來看看 >
的過載,邏輯稍微多一點
1 2 3 4 5 6 7 8 |
func > (left: Vector, right: Vector) -> Bool { if left.x != right.x { return left.x > right.x } if left.y != right.y { return left.y > right.y } if left.z != right.z { return left.z > right.z } //如果上面判斷都失敗了說明 left == right,所以返回值應該是 false return false } |
此時再去比較 V1 和 V2 就會出現你邏輯中的效果。常規的運算子就說到這裡,下面我們來看一下自定義運算子的過載。
Tips:對於運算子的過載我們是不能過載
=
的,它是被編譯器固定擁有的,在底層它是與記憶體管理相關的,,我們不能認為的去改變它,這裡需要注意一下(或者你可以別把賦值運算=
看成運算子 +_+)。
注意以下這些標記=
、->
、//
、/*
、*/
、.
、、
&
、?
、?(中綴運算子)
、>(字尾運算子)
、!
、?
是被系統保留的。這些符號不能被過載,也不能用於自定義運算子。
三、自定義運算子的過載
上面我們所說的都是 Swift 中已經存在了的運算子,那麼我們能不能自己定義運算子呢?答案是肯定的,在文件中我們可以看到這麼一句話
1 2 3 |
Custom operators can begin with one of the ASCII characters /, =, -, +, !, *, %, , &, |, ^, ?, or ~, or one of the Unicode characters defined in the grammar below (which include characters from the *Mathematical Operators*, *Miscellaneous Symbols*, and *Dingbats* Unicode blocks, among others). After the first character, combining Unicode characters are also allowed. |
意思就是
1 2 3 |
自定義運算子可以由以下其中之一的 ASCII 字元 /、=、 -、+、!、*、%、、&、|、^、? 以及~,或者後面語法中規定的任一個 Unicode 字元 (其中包含了*數學運算子*、*零散符號(Miscellaneous Symbols)* 以及印刷符號 (Dingbats) 之類的 Unicode 塊)開始。在第一個字元之後,允許使用組合型 Unicode 字元。 |
- 1、自定義單目運算子
所以我們在自定義運算子的時候要注意一下。下面我們就來簡單的自定義一個單目運算子+++
,這個運算子的作用呢就是讓 Vector 中的每個變數都加 1 ,如下
1234prefix func +++ (vector: Vector) -> Vector {return Vector(x: vector.x + 1, y: vector.y + 1, z: vector.z + 1)}
但是編譯的時候會報錯,如下這是因為我們沒有明確的定義這個
+++
,所以編譯器不識別。所以我們應該申明一下這個運算子,正確的程式碼如下12345prefix operator +++prefix func +++ (vector: Vector) -> Vector {return Vector(x: vector.x + 1, y: vector.y + 1, z: vector.z + 1)}在前面我們用
prefix operator +++
宣告前置運算子 +++ ,這樣後面就可以用了123456var V3 = Vector(x: 1, y: 1, z: 1)prefix operator +++prefix func +++ (vector: Vector) -> Vector {return Vector(x: vector.x + 1, y: vector.y + 1, z: vector.z + 1)}V3+++如上輸出結果就是 Vector(x: 2, y: 2, z: 2),到此,單目運算子的自定義就完成了。
- 2、自定義雙目運算子
雙目運算子的定義和單目運算子的定義類似,但是雙目運算子自定義的時候的關鍵字是infix
,如下1234infix operator **func ** (x: Double, y: Double) -> Double {return pow(x, y)}上面我們就自定義了一個求平方的雙目運算子
**
,然後我們試試2 ** 2
就可以看到結果是4.0
。
上面好像沒有什麼問題了,下面我想算一個平方的平方,撥入 2 的 2次方的 3 次方,照著邏輯應該這樣寫12 ** 2 ** 3但是編譯我們會發現報錯,如下
錯誤是說我們上面的運算是個非結合性的運算,所謂的結合性(associativity)就是運算的先後順序,在 Swift 2 中我們都知道還有個優先順序(precedence),預設的是 100 ,它的範圍是 0~200 ,這個是用來設定運算子優先順序的,比如在swift 2.2 中我們完全定義一個求平方運算子就是
123456infix operator ** { associativity left precedence 120 }func ** (x: Double, y: Double) -> Double {return pow(x, y)}2 ** 2 ** 3 //結果是64.0在 Swift 3 中有些變化,如下
123456789precedencegroup ComparativePrecedence {associativity: righthigherThan: LogicalConjunctionPrecedence}infix operator ** : ComparativePrecedencefunc ** (x: Double, y: Double) -> Double {return pow(x, y)}如上我們輸入
2 ** 2 ** 3
,就會發現結果是 256.0,這是因為我們把 associativity 設定成為了 right,所以運算從右邊開始,先算2 ** 3 = 8.0
,然後再是2 ** 8.0 = 256.0
,如果我們把 associativity 設定成為了 left,就會發現結果是 64.0。關於更多的 associativity 和 higherThan 或者 lowerThan 之類的可以在下方參考連線中參考,這裡就不一一說明了。
差不多運算子過載就到這裡了,如果還有什麼遺漏,歡迎大家指正!
參考:
1、swift-evolution
2、Operator Declaration