【发布时间】:2011-02-14 19:37:05
【问题描述】:
通过值或指针访问另一个结构有什么区别?
什么时候应该使用它们中的每一个?
type foo_ struct {
st uint8
nd uint8
}
type bar struct {
rd uint8
foo foo_
}
type barP struct {
rd uint8
foo *foo_
}
【问题讨论】:
通过值或指针访问另一个结构有什么区别?
什么时候应该使用它们中的每一个?
type foo_ struct {
st uint8
nd uint8
}
type bar struct {
rd uint8
foo foo_
}
type barP struct {
rd uint8
foo *foo_
}
【问题讨论】:
如果您声明或分配type bar 的变量,则为rd uint8 和foo foo_ 保留并初始化为零内存。在type bar的变量中总是嵌入了一个type foo_的变量。
var b bar // declare b
如果您声明或分配type barP 的变量,则为rd uint8 和foo *foo_ 保留并初始化为零内存。零值指针是nil 指针。没有分配type foo_ 的变量;你必须分开做。 type barP 的变量指向了零个 (foo == nil) 或 type foo_ 的一个变量。 type barP 的变量可能与type barP 的其他变量一样指向type foo_ 的同一个变量,共享type foo_ 变量的同一个副本。指向共享副本的所有变量都可以看到对共享副本的更改。
var bp barP // declare bp
bp.foo = new(foo_) // allocate bp.foo
使用哪一个取决于type bar 与type barP 的属性。哪种类型更能反映您要解决的问题?
例如,考虑这个发票问题。我们总是有一个帐单地址;我们总是要我们的钱。但是,我们经常运送到帐单地址,但并非总是如此。如果送货地址是nil,请使用帐单地址。否则,请使用单独的送货地址。我们有两个仓库,我们总是从一个仓库发货。我们可以共享两个仓库位置。由于我们在订单从仓库发货之前不会发送发票,因此仓库位置永远不会是nil。
type address struct {
street string
city string
}
type warehouse struct {
address string
}
type invoice struct {
name string
billing address
shipping *address
warehouse *warehouse
}
【讨论】:
答案在很大程度上与语言无关 - C 中的等价物也有同样的问题。
当你有一个嵌入值时(如bar),那么你的结构就足够大,可以容纳完整的子结构和其他部分。
当你有一个指向一个值的指针时(如barP),那么许多barP 类型的结构可以共享同一个foo。当任何barP 修改它指向的foo 的一部分时,它会影响指向同一位置的所有其他barP 结构。此外,正如评论所暗示的,您必须管理两个单独的对象 - barP 和 foo,而不是使用普通的 bar 类型。
在某些语言中,您将不得不担心悬空指针和未初始化的值等; Go 是垃圾收集的,通常比其他语言更安全。
因此,当您希望多个barP 对象共享同一个foo 对象时,请使用指针;否则,请使用显式成员对象,而不是指向对象的指针。
【讨论】:
type barP的变量被声明或分配时,foo的初始值为nil指针。
Golang FAQ 现在总结了两者之间的区别:
func (s *MyStruct) pointerMethod() { } // method on pointer
func (s MyStruct) valueMethod() { } // method on value
首先,也是最重要的,方法是否需要修改接收器?
如果是,接收者必须是一个指针。 (切片和映射是引用类型,所以它们的故事有点微妙,但是例如要在方法中更改切片的长度,接收器仍然必须是指针。)
在上面的例子中,如果pointerMethod修改了s的字段,调用者会看到这些变化,但是valueMethod是用调用者参数的副本调用的(这是传递值的定义),所以改变它make 对调用者是不可见的。
顺便说一句,指针接收器与 Java 中的情况相同,尽管在 Java 中指针隐藏在幕后。不同寻常的是 Go 的值接收器。其次是考虑效率。如果接收器很大,例如一个大结构体,使用指针接收器会便宜很多。
(这个效率点也在“Memory, variables in memory, and pointers ”中说明)
接下来是一致性。如果该类型的某些方法必须具有指针接收器,则其余的也应该具有,因此无论该类型如何使用,方法集都是一致的。有关详细信息,请参阅method sets 部分。
【讨论】: