Go高性能之方法接收器 - 指针vs值

示例

如果您是 Go 新手,那么您一定遇到过方法和函数的概念。让我们找出两者之间的区别-

通过指定参数的类型、返回值和函数体来声明函数。

type Person struct { 
    Name string 
    Age int 
}func NewPerson(name string, age int) *Person { 
  return &Person{ 
     Name: name, 
     Age: age, 
  } 
}

方法只是一个带有接收器参数的函数。它使用相同的语法声明,并添加了接收者

func (p *Person) isAdult bool { 
  return p.Age > 18 
}

在上面的方法声明中,我们在类型上声明了isAdult方法。*Person

现在我们将看到值接收器指针接收器之间的区别

值接收者复制类型并将其传递给函数。函数堆栈现在拥有一个相等的对象,但在内存上的不同位置。这意味着对传递的对象所做的任何更改都将保留在该方法的本地。原始对象将保持不变。

指针接收器将类型的地址传递给函数。函数堆栈具有对原始对象的引用。因此对传递对象的任何修改都会修改原始对象。

让我们通过示例来理解这一点-

package main
import (
  "fmt"
)
type Person struct {
    Name string
    Age  int
}
func ValueReceiver(p Person) {
    p.Name = "John"
    fmt.Println("Inside ValueReceiver : ", p.Name)
}
func PointerReceiver(p *Person) {
    p.Age = 24
    fmt.Println("Inside PointerReceiver model: ", p.Age)
}
func main() {
    p := Person{"Tom", 28}
    p1:= &Person{"Patric", 68}
    ValueReceiver(p)
fmt.Println("Inside Main after value receiver : ", p.Name)
    PointerReceiver(p1)
fmt.Println("Inside Main after value receiver : ", p1.Age)
}

------------
Inside ValueReceiver :  John
Inside Main after value receiver :  Tom
Inside PointerReceiver :  24
Inside Main after pointer receiver :  24

这表明具有值接收者的方法修改了对象的副本,而原始对象保持不变。Like- 通过 ValueReceiver 方法将一个人的姓名从 Tom 更改为 John,但这种更改并未反映在 main 方法中。另一方面,带有指针接收器的方法会修改实际对象。Like- 通过 PointerReceiver 方法将人的年龄从 68 岁更改为 24 岁,同样的变化反映在 main 方法中。您可以通过在指针或值接收器操作之前和之后打印出对象的地址来检查事实。

那么如何在 Pointer 和 Value 接收器之间进行选择呢?

如果要更改方法中接收器的状态,操作它的值,请使用指针接收器。使用按值复制的值接收器是不可能的。对值接收器的任何修改对于该副本都是本地的。如果您不需要操作接收器值,请使用值接收器

指针接收器避免在每个方法调用上复制值。如果接收器是一个大型结构,这可能会更有效,

值接收器是并发安全的,而指针接收器不是并发安全的。因此,程序员需要照顾它。

汇总:

  • 如果接收者是 map、func 或 chan,不要使用指向它的指针。
  • 尽量对所有方法使用相同的接收器类型。
  • 如果接收者是一个切片并且该方法没有重新切片或重新分配切片,则不要使用指向它的指针。
  • 如果方法需要改变接收者,接收者必须是一个指针。
  • 如果接收者是包含sync.Mutex或类似同步字段的结构,则接收者必须是指针以避免复制。
  • 如果接收器是大型结构或数组,则指针接收器效率更高。大有多大?假设它相当于将其所有元素作为参数传递给方法。如果感觉太大,那么对于接收器来说也太大了。
  • 函数或方法是否可以同时或在从此方法调用时改变接收者?调用方法时,值类型会创建接收器的副本,因此外部更新不会应用于此接收器。如果更改必须在原始接收器中可见,则接收器必须是指针。
  • 如果接收器是结构体、数组或切片,并且它的任何元素都是指向可能发生变化的东西的指针,则更喜欢指针接收器,因为它会使读者更清楚意图。
  • 如果接收者是一个小数组或结构,它自然是一个值类型(例如,类似time.Time类型),没有可变字段和指针,或者只是一个简单的基本类型,如 int 或 string,则值接收器更好
    值接收器可以减少可以生成的垃圾量;如果将值传递给值方法,则可以使用堆栈上的副本而不是在堆上分配。(编译器试图巧妙地避免这种分配,但它并不总是成功。)不要在没有首先进行分析的情况下选择值接收器类型。
  • 最后,当有疑问时,使用指针接收器。

 

原文: https://medium.com/globant/go-method-receiver-pointer-vs-value-ffc5ab7acdb

posted @ 2022-08-01 19:58  sunsky303  阅读(969)  评论(0编辑  收藏  举报
显示搜索