大家好,这里是qsharing。
很多初学go语言的同学(当然也包括我自己)在学习到go结构体和方法的时候通常会遇到一个问题,结构体和结构体指针调用方法的时候到底应该用哪一个?根据官方的FAQ[1]并结合实际代码整理出这篇文章,希望能带给大家一些认识和思考
什么是结构体?
结构体struct是Go语言中常见的其中一个复合类型,举个例子:
type Student struct {
Name string
}
这样就定义了一个结构体。
那么在实际开发中,我们会基于这个结构体构建一个实例:
package main
import "fmt"
type Student struct {
Name string
}
func main() {
s1 := Student{
Name: "qsharing",
Age: 18,
}
fmt.Println(s1.Name)
}
程序运行的结果:
qsharing
结构体和结构体指针
接下来就是本文的主题,在实际开发中,我们通常会对结构体赋予一些方法,例如Setter,如下例子:
type Student struct {
Name string
}
func (s Student) SetNameMethod1(name string) {
s.Name = name
}
func (s *Student) SetNameMethod2(name string) {
s.Name = name
}
这段程序里面包含了两个结构体的方法,两个方法唯一的区别是一个是结构体,一个是结构体指针,那么两者在实际运行中有什么区别呢?
package main
import "fmt"
type Student struct {
Name string
}
func (s Student) SetNameMethod1(name string) {
s.Name = name
}
func (s *Student) SetNameMethod2(name string) {
s.Name = name
}
func main() {
// 构造结构体对象
s1 := Student{
Name: "student1",
}
s1.SetNameMethod1("qsharing")
fmt.Println("调用结构体方法SetNameMethod1:", s1.Name)
// 构造结构体指针
s2 := &Student{
Name: "student2",
}
s2.SetNameMethod2("qsharing")
fmt.Println("调用结构体指针方法SetNameMethod2:", s2.Name)
}
程序运行结果:
调用结构体方法SetNameMethod1: student1
调用结构体指针方法SetNameMethod2: qsharing
区别
可以看出,虽然两个方法的程序逻辑是一致的,但是调用结构体指针的方法才能正确的设置Name,这是为什么呢?
因为作用于SetNameMethod1的结构体实际上是一个结构体的拷贝对象,修改这个拷贝对象的值不会影响原始对象的值。而作用于SetNameMethod2的结构体实际是指向结构体的同一块内存地址,因此修改结构体指针的值会正确反应到原始对象。
另外,使用结构体指针传递给方法,go语言会复制该变量的指针地址作为方法的地址(指向同一块内存地址)。而使用结构体传递给方法,go语言会先完整拷贝这个结构体,再传递给结构体的方法,从效率上来说,传递一个指针地址会比传递一个结构体拷贝要高效很多。
总结
简单来说,我们在使用结构体方法的时候需要注意:
- 该方法是否需要修改接收器?当需要改变结构体的内部元素值,那么就必须使用结构体指针的方法进行调用
- 效率。如果结构体的值很大,那么传递一个结构体地址通常会比传递一个结构体效率更高
- 一致性。如果该结构体的某些方法必须有指针接收器,其余的也应该如此,这样无论如何使用该结构体,方法集都是一致的
最后希望今天的文章能解答到同学们的疑惑。
参考资料
[1]
官方的FAQ: https://golang.org/doc/faq#methods_on_values_or_pointers