在大部分面向对象语言如C++、C#、Java,在函数传参数时除了基础值类型,对象是经过引用方式传递的。shell
然而,在Go语言中,除了map、slice和chan,全部类型(包括struct)都是值传递的。编程
那么,如何在函数外使用函数内处理后的变量呢?只能经过返回新变量吗?框架
不,可使用指针函数
大部分面向对象语言都不多有用到指针的场景了,可是在Go语言中有大量的指针应用场景,要成为一名合格的Gopher,必须了解。学习
每个变量都会分配一块内存,数据保存在内存中,内存有一个地址,就像门牌号,经过这个地址就能够找到里面存储的数据。spa
指针就是保存这个内存地址的变量。指针
在Go语言中,用&
取得变量的地址code
//为了说明类型,我采用了显性的变量定义方法,实际开发中更多的是用“:=”自动获取类型变量类型
var mystr string = "Hello!"
var mystrP *string = &mystr
fmt.Println(mystrP)
复制代码
将以上代码敲入main函数中,go run
,打印出的内容就是mystr
的内存地址。mystrP
就是mystr
的指针变量。cdn
用*
取得指针变量指向的内存地址的值对象
在以前的代码的后面增长一句代码:
fmt.Println(*mystrPointer)
复制代码
go run
运行后,能够看到打印出 mystr
的值“Hello!”
符号*
也用作定义指针类型的关键字。
例如:
var p *int
复制代码
在其余OOP语言中,大多数状况是不须要花太多时间操做指针的,如Java、C#,对象的引用操做都已经交给了虚拟机和框架。而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)++
}
复制代码
*int
替代 int
,申明参数是一个int类型的指针类型i
,而要传递用&
取得i
的地址count
如今是指针了,不能直接打印,须要用*
取出这个指针指向的地址里保存的值count
的取值+1.main
里打印i
。能够看到结果
0
1
复制代码
i
的值改变了。
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
直接实现了接口定义的ChangeName
和SayMyName
两个方法,而mystruct
只能实现了SayMyName
,mystruct
调用ChangeName
方法其实转换成指针类型后调用的,不算实现了接口。
到此Go语言指针类型的应用介绍差很少了。
欢迎你们一块儿讨论、学习Go语言!!
欢迎关注公众号,和你们一块儿学习编程吧