golang的值接收者和指针接收者的区别

Go语言中,值接收者和指针接收者在方法定义上有所区别,影响着方法调用时的对象状态。值接收者的方法调用不会影响原始对象,而指针接收者的方法可以修改原始对象。接口实现时,值类型必须实现所有接口要求的方法,包括值接收者和指针接收者的方法。通常,当需要修改对象或提高效率时,选择指针接收者;否则,使用值接收者。
摘要由CSDN通过智能技术生成

golang的值接收者和指针接收者的区别
方法
值接收者和指针接收者
两者分别在何时使用
方法
方法能给用户自定义的类型添加新的行为。方法和函数的区别在于方法有一个接收者,给一个函数添加一个接收者,那么它就变成了方法。接收者可以是值接收者,也可以是指针接收者。

我们在调用方法的时候,值类型既可以调用值接收者的方法,也可以调用指针接收者的方法;
指针类型既可以调用指针接收者的方法,也可以调用值接收者的方法。

也就是说,不管方法的接收者是什么类型,该类型的值和指针都可以调用,不必严格符合接收者的类型。下面的一个实例可以验证这个结论:

package main

import "fmt"

type Person struct {
    name string
    age int
}

func (p Person) howOld() int {
    fmt.Printf("internal howOld, addrss is %p \n", &p)
    return p.age
}

func (p Person) howOld2() int {
    fmt.Printf("internal howOld2, addrss is %p \n", &p)
    return p.age
}

func (p *Person) growUp() {
    fmt.Printf("internal growUp, addrss is %p \n", p)
    p.age += 1
}


func main()  {
    yuting := Person{
        name: "yuting",
        age: 18,
    }
    fmt.Printf("person, addrss is %p \n", &yuting)
    yuting.howOld()
    yuting.howOld2()
    yuting.growUp()
    fmt.Printf("person, addrss is %p \n", &yuting)
    
    fmt.Println("------------- 2 -------------")
    yt := &Person{
        name: "yuting",
        age: 18,
    }
    fmt.Printf("person, addrss is %p \n", yt)
    yt.howOld()
    yt.howOld2()
    yt.growUp()
    fmt.Printf("person, addrss is %p \n", yt)
}
 
 
我们看看这个实例的执行结果:

person, addrss is 0xc00000c040 
internal howOld, addrss is 0xc00008a020 
internal howOld2, addrss is 0xc00000c060 
internal growUp, addrss is 0xc00000c040 
person, addrss is 0xc00000c040 
------------- 2 -------------
person, addrss is 0xc00000c080 
internal howOld, addrss is 0xc00000c0a0 
internal howOld2, addrss is 0xc00008a040 
internal growUp, addrss is 0xc00000c080 
person, addrss is 0xc00000c080 
 
从打印出的地址可以看出:

对于值接收者,如果调用者也是值对象,那么会将调用者的值拷贝一份,并执行方法,方法的调用不会影响到调用者值。 如果调用者是指针对象,那么会解引用指针对象为值,然后将解引的对象拷贝一份,然后 执行方法。
对于指针接收者,如果调用者是值对象,会使用值的引用来调用方法,上例中,yuting.growUp() 实际上是 、(&yuting).growUp(), 所以传入指针接收者方法的对象地址和 调用者地址一样。如果调用者是指针对象,实际上也是“传值”,方法里的操作会影响到调用者,类似于指针传参,拷贝了一份指针,但是指针指向同一个对象。
用一个表格来表示:

值接受者    指针接收者
值调用者    方法会使用调用者的一个副本,类似于“传值“    使用值的引用来调用方法,上例中,yuting.growUp() 实际上是 (&yuting).growUp()
指针调用者    指针被解引用为值,上例中,yt.howOld() 实际上是 (*yt).howOld()    实际上也是“传值”,方法里的操作会影响到调用者,类似于指针传参,拷贝了一份指针
值接收者和指针接收者
前面说过,不管接收者类型是值类型还是指针类型,都可以通过值类型或指针类型调用,这里面实际上通过语法糖起作用的。

先说结论:实现了接收者是值类型的方法,相当于自动实现了接收者是指针类型的方法;而实现了接收者是指针类型的方法,不会自动生成对应接收者是值类型的方法。

先来看个例子:

package main

import "fmt"

type coder interface {
    howOld() int
    howOld2() int
    growUp()
}

type Gopher struct {
    name string
    age int
}

func (p Gopher) howOld() int {
    fmt.Printf("internal howOld, addrss is %p \n", &p)
    return p.age
}

func (p Gopher) howOld2() int {
    fmt.Printf("internal howOld2, addrss is %p \n", &p)
    return p.age
}

func (p *Gopher) growUp() {
    fmt.Printf("internal growUp, addrss is %p \n", p)
    p.age += 1
}


func main()  {
    fmt.Println("------------- 1 -------------")
    var yt coder = &Gopher{
        name: "yuting",
        age: 18,
    }
    fmt.Printf("person, addrss is %p \n", yt)
    yt.howOld()
    yt.howOld2()
    yt.growUp()
    fmt.Printf("person, addrss is %p \n", yt)
    
    fmt.Println("------------- 2 -------------")
    var yt coder = Gopher{
        name: "yuting",
        age: 18,
    }
    //fmt.Printf("person, addrss is %p \n", &yuting)
    //yuting.howOld()
    //yuting.howOld2()
    //yuting.growUp()
    //fmt.Printf("person, addrss is %p \n", &yuting)
}
 
 
 
上面定义了一个接口:

type coder interface {
    howOld() int
    howOld2() int
    growUp()
}
 
定义了结构体:Gopher,它实现了两个方法,两个值接收者,一个指针接收者。最后,我们在 main 函数里通过接口类型的变量(Gopher指针)调用了定义的两个函数。运行正常。

但是如果我们把 coder interface 变量的值改成 Gopher 实例,就会编译失败,报错:

cannot use Gopher literal (type Gopher) as type coder in assignment:
    Gopher does not implement coder (growUp method has pointer receiver)
 
提示显示:Gopher 对象没有实现coder 接口的 growUp 方法,因为growUp有一个指针接收者。

简单的说就是,*Gopher 实现了coder, 但是Gopher没有实现 coder。

有一个简单的解释:接收者是指针类型的方法,很可能在方法中会对接收者的属性进行更改操作,从而影响接收者;而对于接收者是值类型的方法,在方法中不会对接收者本身产生影响。所以,当实现了一个接收者是值类型的方法,就可以自动生成一个接收者是对应指针类型的方法,因为两者都不会影响接收者。但是,当实现了一个接收者是指针类型的方法,如果此时自动生成一个接收者是值类型的方法,原本期望对接收者的改变(通过指针实现),现在无法实现,因为值类型会产生一个拷贝,不会真正影响调用者。

结论就是:如果实现了接收者是值类型的方法,会隐含地也实现了接收者是指针类型的方法。

两者分别在何时使用
如果方法的接收者是值类型,无论调用者是对象还是对象指针,修改的都是对象的副本,不影响调用者;如果方法的接收者是指针类型,则调用者修改的是指针指向的对象本身。

使用指针作为方法的接收者的理由:

方法能够修改接收者指向的值。
避免在每次调用方法时复制该值,在值的类型为大型结构体时,这样做会更加高效。


 转自golang的值接收者和指针接收者的区别_惜暮-CSDN博客

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值