Golang - 函数和方法的区别【作用于值类型、指针类型】

  在其他语言中,比如Java一般来说,函数就是方法,方法就是函数;

  但是在Go语言中,函数和方法不太一样,有明确的概念区分。函数是指不属于任何结构体类型的方法,即函数是没有接受者的;而方法是有接收者的。

1、函数

func add (a, b int ) int {

  return a+b

}

2、方法(特殊的函数)

func (t *T) add (a, b int ) int {

  return a+b

}

  其中T是自定义类型或者结构体,不能是基础类型int等。

3、方法的接收者

  Go语言里方法有两种类型的接收者:值接收者和指针接收者。

1)、使用值类型接收者定义的方法,在调用的时候,使用的其实是值接收者的一个副本,所以对该值的任何操作,不会影响原来的类型变量。

复制代码
func main() {
    p:=person{name:"张三"}
    p.modify()     //值接收者,修改无效
    fmt.Println(p.String())
}
 
type person struct {
    name string
}
 
func (p person) String() string{
    return "the person name is "+p.name
}
 
func (p person) modify(){
    p.name = "李四"
}
复制代码

2)、如果我们使用一个指针作为接收者,那么就会起作用,因为指针接收者传递的是一个指向原值指针的副本,指针的副本指向的还是原来类型的值,所以修改时,同时也会影响原来类型变量的值。

复制代码
func main() {
    p:=person{name:"张三"}
    p.modify()   //指针接收者,修改有效
    fmt.Println(p.String())
}
 
type person struct {
    name string
}
 
func (p person) String() string{
    return "the person name is "+p.name
}
 
func (p *person) modify(){
    p.name = "李四"
}
复制代码

总结:

  在调用方法的时候,传递的接收者本质上都是副本,一个是这个值副本,另一个是指向这个值指针的副本。指针具有指向原有值的特性,所以修改了指针指向的值,也就修改了原有的值。我们可以简单的理解为值接收者使用的是值的副本来调用方法,而指针接收者使用实际的值来调用方法。

4、如何选择 P 和 *P

  一般的判断标准是看副本创建的成本和需求:

1)、不想变量被修改。 如果你不想变量被函数和方法所修改,那么选择类型P。相反,如果想修改原始的变量,则选择*P;
2)、如果变量是一个大的struct或者数组,则副本的创建相对会影响性能,这个时候考虑使用*P,只创建新的指针,这个区别是巨大的;
3)、(不针对函数参数,只针对本地变量/本地变量)对于函数作用域内的参数,如果定义成P,Go编译器尽量将对象分配到栈上,而*P很可能会分配到对象上,这对垃圾回收会有影响。

5、函数、方法作用于不同类型接收者的使用场景差异

1)、对于普通函数,接收者为值类型时,不能将指针类型的数据直接传递;反之亦然。(接收者为指针类型时,不能将值类型的数据直接传递)

复制代码
//1.普通函数
//接收值类型参数的函数
func valueIntTest(a int) int {
    return a + 10
}

//接收指针类型参数的函数
func pointerIntTest(a *int) int {
    return *a + 10
}

func structTest1() {
    a := 2
    fmt.Println("valueIntTest:", valueIntTest(a))
    //函数的参数为值类型,则不能直接将指针作为参数传递
    //fmt.Println("valueIntTest:", valueIntTest(&a))
    //compile error: cannot use &a (type *int) as type int in function argument

    b := 5
    fmt.Println("pointerIntTest:", pointerIntTest(&b))
    //同样,当函数的参数为指针类型时,也不能直接将值类型作为参数传递
    //fmt.Println("pointerIntTest:", pointerIntTest(b))
    //compile error:cannot use b (type int) as type *int in function argument
}
复制代码

2)、对于方法(如struct的方法),接收者为值类型时,可以直接用指针类型的变量调用方法,反过来同样也可以。(接收者为指针类型和值类型的方法,指针类型和值类型的变量均可相互调用)

复制代码
//2.方法
type Person struct {
    id   int
    name string
}

//接收者为值类型
func (p Person) valueShowName() {
    fmt.Println(p.name)
}

//接收者为指针类型
func (p *Person) pointShowName() {
    fmt.Println(p.name)
}

func structTest2() {
    //值类型调用方法
    personValue := Person{101, "Will Smith"}
    personValue.valueShowName()
    personValue.pointShowName()

    //指针类型调用方法
    personPointer := &Person{102, "Paul Tony"}
    personPointer.valueShowName()
    personPointer.pointShowName()

    //与普通函数不同,接收者为指针类型和值类型的方法,指针类型和值类型的变量均可相互调用
}
复制代码
posted @   李若盛开  阅读(260)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· PowerShell开发游戏 · 打蜜蜂
· 在鹅厂做java开发是什么体验
· 百万级群聊的设计实践
· WPF到Web的无缝过渡:英雄联盟客户端的OpenSilver迁移实战
· 永远不要相信用户的输入:从 SQL 注入攻防看输入验证的重要性
点击右上角即可分享
微信分享提示