一、重载运算符给函数“加戏”,我先带你跑通第一个 demo
我带过的新手学员,十有八九都被“重载”这个词唬住过。
重载说白了就是同一个函数名,干不同的事。
你先看这段能直接跑的代码:
// 运行环境:Swift 5.0+,Xcode 14+
// 函数重载最简版:两个同名函数,参数类型不同
// 整数加法
func addFunc(_ a: Int, _ b: Int) -> Int {
return a + b
}
// 浮点数加法(同名函数)
func addFunc(_ a: Double, _ b: Double) -> Double {
return a + b
}
// 调用
let intResult = addFunc(10, 20) // 30
let doubleResult = addFunc(3.5, 2.5) // 6.0
print("整型结果:(intResult),浮点型结果:(doubleResult)")
学员最常踩的坑是:以为重载就是改返回值类型。
错!
Swift 编译器所关注的是具有特定类型的参数,以及参数的数量多少,还有参数所带有的标签,返回值不一样的情况并不被算作重载。

在下述代码当中,某一个参数属于 Int 类型,而另外一个乃是 Double 类型,如此这般编译器方可明确知晓该调用哪一个。
二、运算符重载就是“给+号加新功能”,直接上 Circle 类实战

你每日都在使用的那个+号,实际上也是经过重载而得来的,它能够对整数进行加法运算,能够对浮点数进行加法运算,并且还能够拼接字符串。
下面我们让+号能加圆形:

// 圆形类:半径 + 圆心
class Circle {
var radius: Double
var centerX: Double
var centerY: Double
init(radius: Double, centerX: Double, centerY: Double) {
self.radius = radius
self.centerX = centerX
self.centerY = centerY
}
}
// 重载 + 运算符:半径相加,圆心取第一个圆
func + (left: Circle, right: Circle) -> Circle {
let newRadius = left.radius + right.radius
return Circle(radius: newRadius,
centerX: left.centerX,
centerY: left.centerY)
}
// 测试
let circle1 = Circle(radius: 5, centerX: 0, centerY: 0)
let circle2 = Circle(radius: 3, centerX: 10, centerY: 10)
let resultCircle = circle1 + circle2
print("新圆半径:(resultCircle.radius)") // 8.0
我带着学员踩过的真切存在的坑,有个学员在进行+号重载操作的时候,把处理边界条件这件事给忘掉了 —— 两个呈现 nil 状态的对象相加,直接就崩溃了。
正确做法要加guard判断。
企业级优化版应该这样写:
// 企业版:支持可选类型 + 异常处理
func + (left: Circle?, right: Circle?) -> Circle? {
guard let left = left, let right = right else {
print("警告:存在 nil 对象,返回 nil")
return nil
}
let newRadius = left.radius + right.radius
return Circle(radius: newRadius,
centerX: left.centerX,
centerY: left.centerY)
}

三、自定义运算符:手搓一个“++”,分三步走
Swift 移除++后,我们可以自己加回来。
自定义运算符分三步:声明→类型→实现。
// 第一步:声明自定义运算符
prefix operator ++
// 第二步+第三步:实现前缀++(先自加后返回)
prefix func ++ (value: inout Int) -> Int {
value += 1
return value
}
// 测试
var num = 10
let newNum = ++num
print("num: (num), newNum: (newNum)") // num: 11, newNum: 11

中缀运算符(两个操作数)写法不同,不需要写prefix:
// 自定义中缀运算符:求两个数的平均值
infix operator ±±
func ±± (left: Double, right: Double) -> Double {
return (left + right) / 2
}
let avg = 10.0 ±± 20.0
print("平均值:(avg)") // 15.0
func myFunc(closure:(Circle,Circle)->Circle) {
}
//将重载的加法运算符传入
myFunc(closure: +)
存在这样一种学员常常会踩到的坑,那就是在进行自定义运算符操作的时候,使用了 Swift 所保留的字符,像那些以问号、感叹号开头的字符,一旦这样做,编译的时候就会直接出现报错的情况。
此外存在个隐蔽的坑,前缀运算符以及后缀运算符的参数必定得是 inout,不然的话就无法改变原来的值。
四、运算符类型怎么选?

一张表帮你避坑
| 运算符类型 | 关键字 | 参数个数 | 示例 |
|---|---|---|---|
| 前缀 | prefix |
1 个 | ++a |
| 中缀 | infix |
2 个 | a + b |
| 后缀 | postfix |
1 个 | a++ |
选型得出这样的结论,在百分之九十的场景当中,会使用中缀运算符,像是自定义向量加法、矩阵乘法这种情形。
前缀/后缀主要用于自增自减、逻辑取反。
我曾带过的学员,老是想着运用后缀运算符去开展复杂的计算,然而呢,结果是参数根本没办法去传递第二个值,最终啊,全都白忙活一场了。

记住:后缀只有一个操作数,想传两个参数必须用中缀。
五、最后给你一个排错清单
你在写重载或自定义运算符时,遇到报错按这个顺序查:
1. 运算符声明了吗(prefix operator ++这种)
2. 参数类型匹配吗(重载靠参数区分,不是靠返回值)
3. 中缀/前缀/后缀关键字写对了吗
4. 用了保留字符开头吗(?、!、=单独不能用)
5. 修改原值加inout了吗
拿上面每一段代码,复制之后放入 Playground 里面,随后运行一回,再去更改更改参数,尝试尝试,半小时的时间就能够彻彻底底搞明白。
课后所进行的练习:自行去定义一种运算符的重载,该运算符要能使得 Circle 与 Int 相乘之后返回的结果是半径被扩大了 N 倍的圆。

Comments NOTHING