上卷 程序设计
中卷 标准库
- bufio 1.18
- bytes 1.18
- io 1.18
- container 1.18
- encoding 1.18
- crypto 1.18
- hash 1.18
- index 1.18
- sort 1.18
- context 1.18
- database 1.18
- connection
- query
- queryrow
- exec
- prepare
- transaction
- scan & null
- context
- tcp
- udp
- http
- server
- handler
- client
- h2、tls
- url
- rpc
- exec
- signal
- embed 1.18
- plugin 1.18
- reflect 1.18
- runtime 1.18
- KeepAlived
- ReadMemStats
- SetFinalizer
- Stack
- sync 1.18
- atomic
- mutex
- rwmutex
- waitgroup
- cond
- once
- map
- pool
- copycheck
- nocopy
- unsafe 1.18
- fmt 1.18
- log 1.18
- math 1.18
- time 1.18
- timer
下卷 运行时
源码剖析
附录
文章来源于网络收集而来,版权归原创者所有,如有侵权请及时联系!
6. 方法
方法(method)是与对象实例(instance)相绑定的特殊函数。
方法是面向对象编程的基本概念,用于维护和展示对象自身状态。对象是内敛的,每个实例都有各自不同的独立特征,以属性和方法来对外暴露。普通函数专注于算法流程,接收参数完成逻辑运算,返回结果并清理现场。也就是说,方法有持续性状态,而函数通常没有。
- 前置接收参数(receiver),代表方法所属类型。
- 可为当前包内除接口和指针以外的任何类型定义方法。
- 不支持静态方法(static method)或关联函数。
- 不支持重载(overload)。
func (int) test() {} // ~~~ cannot define new methods on non-local type int // ------------------------- type N *int func (N) test() {} // ~ invalid receiver type N (pointer or interface type) // ------------------------- type M int func (M) test(){} func (*M) test(){} // ~~~~ redeclared in this block
对接收参数命名无限制,按惯例选用简短有意义的名称。
如方法内部不引用实例,可省略接收参数名,仅留类型。
type N int func (n N) toString() string { return fmt.Sprintf("%#x", n) } func (N) test() { // 省略接收参数名。 println("test") } // ----------------------------- func main() { var a N = 25 println(a.toString()) }
接收参数可以是指针类型,调用时据此决定是否复制(pass by value)。
注意区别:
不能为指针和接口定义方法,是说类型
N
本身不能是接口和指针。这与作为参数列表成员的
receiver *N
意思完全不同。方法本质上就是特殊函数,接收参数无非是其第一参数。
只不过,在某些语言里它是隐式的
this
。
type N int func (n N) copy() { fmt.Printf("%p, %v\n", &n, n) } func (n *N) ref() { fmt.Printf("%p, %v\n", n, *n) } // ----------------------------- func main() { var a N = 25 fmt.Printf("%p\n", &a) // 0xc000014080 a.copy() // 0xc000014088, 25 N.copy(a) // 0xc0000140a0, 25 a++ a.ref() // 0xc000014080, 26 (*N).ref(&a) // 0xc000014080, 26 }
编译器根据接收参数类型,自动在值和指针间转换。
type N int func (n N) copy() {} func (n *N) ref() {} //--------------------- func main() { var a N = 25 var p *N = &a a.copy() a.ref() // (*N).ref(&a) p.copy() // N.copy(*p) p.ref() } /* $ go build -gcflags "-N -l" $ go tool objdump -S -s "main\.main" ./test TEXT main.main(SB) func main() { var a N = 25 0x455254 MOVQ $0x19, 0x8(SP) var p *N = &a 0x45525d LEAQ 0x8(SP), CX 0x455262 MOVQ CX, 0x18(SP) a.copy() 0x455267 MOVQ 0x8(SP), AX 0x45526c CALL main.N.copy(SB) a.ref() 0x455271 LEAQ 0x8(SP), AX ; &a 0x455276 CALL main.(*N).ref(SB) p.copy() 0x45527b MOVQ 0x18(SP), CX 0x455282 MOVQ 0(CX), AX 0x45528a CALL main.N.copy(SB) p.ref() 0x45528f MOVQ 0x18(SP), AX 0x455294 CALL main.(*N).ref(SB) } */
不能以多级指针调用方法。
func main() { var a N = 25 var p *N = &a p2 := &p // p2.copy() // ~~~~~~~ p2.copy undefined // p2.ref() // ~~~~~~ p2.ref undefined (*p2).copy() (*p2).ref() }
如何确定接收参数(receiver)类型?
- 修改实例状态,用
*T
。 - 不修改状态的小对象或固定值,用
T
。 - 大对象用
*T
,减少复制成本。 - 引用类型、字符串、函数等指针包装对象,用
T
。 - 含
Mutex
等同步字段,用*T
,避免因复制造成锁无效。 - 其他无法确定的,都用
*T
。
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论