Golang研学:在函数、方法、接口中用好指针类型

在大部分面向对象语言如C++、C#、Java,在函数传参数时除了基础值类型,对象是经过引用方式传递的。shell

然而,在Go语言中,除了map、slice和chan,全部类型(包括struct)都是值传递的。框架

那么,如何在函数外使用函数内处理后的变量呢?只能经过返回新变量吗?函数

不,可使用指针学习

大部分面向对象语言都不多有用到指针的场景了,可是在Go语言中有大量的指针应用场景,要成为一名合格的Gopher,必须了解。指针

概念

每个变量都会分配一块内存,数据保存在内存中,内存有一个地址,就像门牌号,经过这个地址就能够找到里面存储的数据。code

指针就是保存这个内存地址的变量。对象

在Go语言中,用&取得变量的地址接口

//为了说明类型,我采用了显性的变量定义方法,实际开发中更多的是用“:=”自动获取类型变量类型
var mystr string = "Hello!"
var mystrP *string = &mystr

fmt.Println(mystrP)

将以上代码敲入main函数中,go run,打印出的内容就是mystr的内存地址。mystrP就是mystr的指针变量。内存

*取得指针变量指向的内存地址的值开发

在以前的代码的后面增长一句代码:

fmt.Println(*mystrPointer)

go run 运行后,能够看到打印出 mystr的值“Hello!”

符号*也用作定义指针类型的关键字。

例如:

var p *int

指针应用场景

在其余OOP语言中,大多数状况是不须要花太多时间操做指针的,如Java、C#,对象的引用操做都已经交给了虚拟机和框架。而Go常常会用到指针。缘由主要有3个:

  1. Go语言中除了map、slice、chan外,其余类型在函数参数中都是值传递
  2. Go语言不是面向对象的语言,不少时候实现结构体方法时须要用指针类型实现引用结构体对象
  3. 指针也是一个类型,在实现接口interface时,结构体类型和其指针类型对接口的实现是不一样的

接下来就分别介绍一下,期间会穿插一些简单的代码片断,您能够建立一个Go文件输入代码,运行体验一下。

函数中传递指针参数

Go语言都是值传递,例如:

package main

import "fmt"

func main() {
    i := 0
    f(i)
    fmt.Println(i)
}

func f(count int) {
    fmt.Println(count)
    count++
}

结果:

0
0

i在执行先后没有变化

若是但愿被函数调用后,i的值产生变化,f函数的参数就应该改成 *int 类型。如:

func main() {
    i := 0
    f(&i)
    fmt.Println(i)
}

func f(count *int) {
    fmt.Println(*count)
    (*count)++
}
  1. f定义参数用 *int 替代 int,申明参数是一个int类型的指针类型
  2. 调用函数时,不能直接传递int的变量i,而要传递用&取得i的地址
  3. f函数内,参数count如今是指针了,不能直接打印,须要用*取出这个指针指向的地址里保存的值
  4. count的取值+1.
  5. 调用f函数,在主函数main里打印i

能够看到结果

0
1

i的值改变了。

Struct结构体指针类型方法

Go语言中给结构体定义方法

//定义一个结构体类型
type myStruct struct {
    Name string
}

//定义这个结构体的更名方法
func (m myStruct) ChangeName(newName string) {
    m.Name = newName
}

func main() {
    //建立这个结构体变量
    mystruct := myStruct{
        Name: "zeta",
    }

    //调用更名函数
    mystruct.ChangeName("Chow")

    //没改为功
    fmt.Println(mystruct.Name)
}

这样的方法不会改掉结构体变量内的字段值。 就算是结构体方法,若是不使用指针,方法内仍是传递结构体的值。

如今咱们改用指针类型定义结构体方法看看。

只修改 ChangeName 函数,用*myStruct类型替代myStruct

func (m *myStruct) ChangeName(newName string) {
    m.Name = newName
}

再运行一次,能够看到打印出来的名字改变了。

当使用指针类型定义方法后,结构体类型的变量调用方法时会自动取得该结构体的指针类型并传入方法。

指针类型的接口实现

最近在某问答平台回答了一个Gopher的问题,大体内容是问为何不能用结构体指针类型实现接口方法?

看一下代码

//定义一个接口
type myInterface interface {
    ChangeName(string)
    SayMyName()
}

//定义一个结构体类型
type myStruct struct {
    Name string
}

//定义这个结构体的更名方法
func (m *myStruct) ChangeName(newName string) {
    m.Name = newName
}

func (m myStruct) SayMyName() {
    fmt.Println(m.Name)
}

//一个使用接口做为参数的函数
func SetName(s myInterface, name string) {
    s.ChangeName(name)
}

func main() {
    //建立这个结构体变量
    mystruct := myStruct{
        Name: "zeta",
    }

    SetName(mystruct, "Chow")

    mystruct.SayMyName()
}

这段代码是没法编译经过的,会提示

cannot use mystruct (type myStruct) as type myInterface in argument to SetName:
        myStruct does not implement myInterface (ChangeName method has pointer receiver)

myStruct类型没有实现接口方法ChangeName,也就是说func (m *myStruct) ChangeName(newName string) 并不算实现了接口,由于它是*myStruct类型实现的,而不是myStruct

改一改

在调用SetName时,用&mystruct 替代 mystruct:

SetName(&mystruct, "Chow")

编译运行,成功。

为何结构体类型实现的接口该结构体的指针类型也算实现了,而指针类型实现的接口,不算是该结构体实现了接口呢?

缘由是,结构体类型定义的方法能够被该结构体的指针类型调用;而结构体类型调用该指针类型的方法时是被转换成指针,不是直接调用。

因此,&mystruct 直接实现了接口定义的ChangeNameSayMyName两个方法,而mystruct只能实现了SayMyNamemystruct调用ChangeName方法其实转换成指针类型后调用的,不算实现了接口。

    • *

到此Go语言指针类型的应用介绍差很少了。

总结一下:

  1. Go语言中指针很是经常使用,必定要掌握
  2. Go语言除了map、slice、chan其余都是值传递,引用传递必定要用指针类型
  3. 结构体类型定义方法要注意使用指针类型
  4. 接口实现方法时,用指针类型实现的接口函数只能算是指针类型实现的,用结构体类型实现的方法也做为是指针类型实现。

欢迎你们一块儿讨论、学习Go语言!!

相关文章
相关标签/搜索