Swift运算符重载
Swift运算符重载

Swift运算符重载

在 Swift 中,运算符重载是一种功能,允许开发者为自定义类型(如结构体、类或枚举)定义已有运算符的行为。这使得可以使用熟悉的运算符(如 +、-、*、/ 等)直接对自定义类型进行操作,从而简化代码的可读性和表达力。

定义运算符重载

要重载运算符,需要实现一个全局函数,该函数的名字是所需的运算符,并且至少有一个参数是自定义类型。

示例代码

重载 + 运算符

struct Vector {
    var x: Double
    var y: Double

    static func + (lhs: Vector, rhs: Vector) -> Vector {
        return Vector(x: lhs.x + rhs.x, y: lhs.y + rhs.y)
    }
}

let v1 = Vector(x: 1.0, y: 2.0)
let v2 = Vector(x: 3.0, y: 4.0)
let result = v1 + v2 // Vector(x: 4.0, y: 6.0)

定义了 Vector 类型的 + 运算符。

lhs 和 rhs 是左右两个操作数。

重载比较运算符

struct Point {
    var x: Int
    var y: Int

    static func == (lhs: Point, rhs: Point) -> Bool {
        return lhs.x == rhs.x && lhs.y == rhs.y
    }

    static func < (lhs: Point, rhs: Point) -> Bool {
        return lhs.x < rhs.x || (lhs.x == rhs.x && lhs.y < rhs.y)
    }
}

let p1 = Point(x: 1, y: 2)
let p2 = Point(x: 1, y: 3)
print(p1 == p2) // false
print(p1 < p2)  // true

这里重载了 == 和 < 运算符,用于比较两个点的相等性和排序。

重载自定义运算符

可以创建自己的运算符,并对其进行重载。

1、定义自定义运算符

prefix operator ++

2、重载该运算符

struct Counter {
    var count: Int

    static prefix func ++ (value: inout Counter) -> Counter {
        value.count += 1
        return value
    }
}

var counter = Counter(count: 5)
let updatedCounter = ++counter // Counter(count: 6)

复合赋值运算符

可以重载 += 等复合运算符:

struct Vector {
    var x: Double
    var y: Double

    static func += (lhs: inout Vector, rhs: Vector) {
        lhs = Vector(x: lhs.x + rhs.x, y: lhs.y + rhs.y)
    }
}

var v = Vector(x: 1.0, y: 2.0)
let increment = Vector(x: 3.0, y: 4.0)
v += increment // Vector(x: 4.0, y: 6.0)

支持自定义类型的 Comparable

通过重载 < 运算符,可以让自定义类型符合 Comparable 协议:

struct Point: Comparable {
    var x: Int
    var y: Int

    static func < (lhs: Point, rhs: Point) -> Bool {
        return lhs.x < rhs.x || (lhs.x == rhs.x && lhs.y < rhs.y)
    }
}

let p1 = Point(x: 1, y: 2)
let p2 = Point(x: 2, y: 1)
print(p1 < p2) // true

泛型运算符重载

可以使用泛型来重载运算符以支持多种类型:

func +<T: Numeric>(lhs: T, rhs: T) -> T {
    return lhs + rhs
}

let sum = 3 + 4.5 // 结果是 7.5

运算符种类

在重载自定义运算符石,定义的是前缀运算符:

prefix operator ++

运算符根据其放置的位置可以分为三种:

1)前缀运算符(Prefix Operator): 运算符位于操作数之前,例如 -value、++value。

2)后缀运算符(Postfix Operator): 运算符位于操作数之后,例如 value++、value!。

3)中缀运算符(Infix Operator): 运算符位于两个操作数之间,例如 a + b。

prefix operator 的作用

prefix operator ++ 声明了一个新的前缀运算符 ++。此时,运算符仅仅被声明,但还没有实现任何功能。需要为该运算符定义一个实现。

定义和实现 ++ 前缀运算符

prefix operator ++

struct Counter {
    var count: Int

    // 实现 ++ 运算符
    static prefix func ++ (value: inout Counter) -> Counter {
        value.count += 1
        return value
    }
}

var counter = Counter(count: 5)
let updatedCounter = ++counter // Counter(count: 6)
print(counter.count) // 6

1、声明 prefix operator ++

这一步告诉 Swift,++ 是一个新的前缀运算符。

2、实现运算符逻辑

使用 static prefix func ++ 定义运算符的行为:

参数 value 是操作数。

使用 inout 修饰,表明该操作数可以被修改。

value.count += 1 表示操作数 Counter 的 count 属性加 1。

3、使用运算符

当使用 ++counter 时:

counter 的 count 属性加 1。

返回更新后的 Counter 对象。

运算符的使用范围

prefix operator 只能用于前缀操作,不支持作为中缀或后缀运算符使用。如果需要在后缀位置使用,则需要定义 postfix operator。

定义后缀运算符

postfix operator ++

struct Counter {
    var count: Int

    // 实现后缀 ++ 运算符
    static postfix func ++ (value: inout Counter) -> Counter {
        let oldValue = value
        value.count += 1
        return oldValue
    }
}

var counter = Counter(count: 5)
let previousCounter = counter++ // Counter(count: 5)
print(counter.count) // 6

后缀运算符返回操作之前的值。

这是后缀运算符与前缀运算符的主要区别。

定义中缀运算符

infix operator >>>
struct Fang {
    var name: String
    
    static func >>> (left: Fang, right: Fang) -> String {
        return left.name + right.name + "小火车"
    }
}
var fang1 = Fang(name: "fang1")
var fang2 = Fang(name: "fang2")
print(fang1 >>> fang2)  // fang1fang2小火车

中缀运算符与前缀和后缀运算符相比,没有prexfix或postfix,两个参数可以自定义名称,如left或right。

注意事项

1、不要滥用重载: 重载运算符应该具有明确的含义,否则会让代码难以理解。

2、避免歧义: 确保重载运算符的行为是直观的,并且不会与已有运算符行为冲突。

3、性能问题: 复杂的运算符重载可能会对性能产生影响,尤其是泛型或递归调用的情况。

通过合理使用运算符重载,可以让代码更简洁,同时增强可读性。

附录

Swift运算符名称的规则

在 Swift 中,运算符名称的定义必须遵循特定的规则。不符合 Swift 对运算符名称的要求,则会报错:

'fang' is considered an identifier and must not appear within an operator name

因为它被解析为一个普通的标识符(identifier),而不是合法的运算符名称。

1、运算符名称只能由特定的符号组成

运算符必须使用 Swift 支持的 运算符字符,而不是普通的标识符字符。

可用作运算符的符号包括

数学符号:+、-、*、/

比较符号:<、>、=

逻辑符号:&、|、!

其他符号:?、^、%、~

例如:+?、>>>、!== 是有效的运算符名称。

2、不能包含字母或数字

像 fang、op1 这样的名称包含字母或数字,因此会被解析为普通标识符,而不是运算符名称。

3、运算符名称的作用

Swift 中运算符的设计是为了增强代码的数学表达能力,而标识符(如 fang)更适合用于函数、变量等。

如果您认为这篇文章给您带来了帮助,您可以在此通过支付宝或者微信打赏网站开放者。

发表回复

您的电子邮箱地址不会被公开。 必填项已用 * 标注