Golang拾遗之指针和接口的使用详解.docx
第
Golang拾遗之指针和接口的使用详解
目录指针和接口golang的指针指向interface的指针总结
指针和接口
golang的类型系统其实很有意思,有意思的地方就在于类型系统表面上看起来众生平等,然而实际上却要分成普通类型(types)和接口(interfaces)来看待。普通类型也包含了所谓的引用类型,例如slice和map,虽然他们和interface同为引用类型,但是行为更趋近于普通的内置类型和自定义类型,因此只有特立独行的interface会被单独归类。
那我们是依据什么把golang的类型分成两类的呢?其实很简单,看类型能不能在编译期就确定以及调用的类型方法是否能在编译期被确定。
如果觉得上面的解释太过抽象的可以先看一下下面的例子:
packagemain
importfmt
funcmain(){
m:=make(map[int]int)
m[1]=1*2
m[2]=2*2
fmt.Println(m)
m2:=make(map[string]int)
m2[python]=1
m2[golang]=2
fmt.Println(m2)
首先我们来看非interface的引用类型,m和m2明显是两个不同的类型,不过实际上在底层他们是一样的,不信我们用objdump工具检查一下:
gotoolobjdump-smain\.maina
TEXTmain.main(SB)/tmp/a.go
a.go:6CALLruntime.makemap_small(SB)...
a.go:7CALLruntime.mapassign_fast64(SB)#m[1]=1*2
...
a.go:8CALLruntime.mapassign_fast64(SB)#m[2]=2*2
...
...
a.go:10CALLruntime.makemap_small(SB)...
a.go:11CALLruntime.mapassign_faststr(SB)#m2[python]=1
...
a.go:12CALLruntime.mapassign_faststr(SB)#m2[golang]=2
省略了一些寄存器的操作和无关函数的调用,顺便加上了对应的代码的原文,我们可以清晰地看到尽管类型不同,但map调用的方法都是相同的而且是编译期就已经确定的。如果是自定义类型呢?
packagemain
importfmt
typePersonstruct{
namestring
ageint
func(p*Person)sayHello(){
fmt.Printf(Hello,Im%v,%vyear(s)old\n,p.name,p.age)
funcmain(){
p:=Person{
name:apocelipes,
age:100,
p.sayHello()
这次我们创建了一个拥有自定义字段和方法的自定义类型,下面再用objdump检查一下:
gotoolobjdump-smain\.mainb
TEXTmain.main(SB)/tmp/b.go
...
b.go:19CALLmain.(*Person).sayHello(SB)
...
用字面量创建对象和初始化调用堆栈的汇编代码不是重点,重点在于那句CALL,我们可以看到自定义类型的方法也是在编译期就确定了的。
那反过来看看interface会有什么区别:
packagemain
importfmt
typeWorkerinterface{
Work()
typeTypiststruct{}
func(*Typist)Work(){
fmt.Println(Typing...)
typeProgramerstruct{}
func(*Programer)Work(){
fmt.Println(Programming...)
funcmain(){
varwWorker=Typist{}
w.Work()
w=Programer{}
w.Work()
注意!编译这个程序需要禁止编译器进