英文:
Access to another struct by value or by pointer
问题
当通过值或指针访问另一个结构时有什么区别?什么时候应该使用它们中的每一个?
type foo_ struct {
st uint8
nd uint8
}
type bar struct {
rd uint8
foo foo_
}
type barP struct {
rd uint8
foo *foo_
}
英文:
What difference there is when you access to another struct by value or by a pointer?
When should be used each one of them?
type foo_ struct {
st uint8
nd uint8
}
type bar struct {
rd uint8
foo foo_
}
type barP struct {
rd uint8
foo *foo_
}
答案1
得分: 5
如果你声明或分配一个bar
类型的变量,你会为rd uint8
和foo foo_
两个变量预留并初始化为零的内存。在一个bar
类型的变量中,始终嵌入一个foo_
类型的变量。
var b bar // 声明 b
如果你声明或分配一个barP
类型的变量,你会为rd uint8
和foo *foo_
两个变量预留并初始化为零的内存。零值指针是一个nil
指针。不会分配foo_
类型的变量;你必须单独完成这个操作。一个barP
类型的变量要么指向零个(foo == nil
),要么指向一个foo_
类型的变量。一个barP
类型的变量可能指向与其他barP
类型的变量相同的foo_
类型的变量,共享同一个foo_
类型的变量的副本。对共享副本的更改会被所有指向它的变量看到。
var bp barP // 声明 bp
bp.foo = new(foo_) // 分配 bp.foo
使用哪种类型取决于bar
类型与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
}
英文:
If you declare or allocate a variable of type bar
, you reserve and initialize to zero memory for both rd uint8
and foo foo_
. There is always one variable of type foo_
embedded in a variable of type bar
.
var b bar // declare b
If you declare or allocate a variable of type barP
, you reserve and initialize to zero memory for both rd uint8
and foo *foo_
. A zero value pointer is a nil
pointer. No variable of type foo_
is allocated; you must do that separately. There is either zero (foo == nil
) or one variable of type foo_
pointed to by a variable of type barP
. A variable of type barP
may point to the same variable of type foo_
as other variables of type barP
, sharing the same copy of the variable of type foo_
. A change to a shared copy is seen by all variables that point to it.
var bp barP // declare bp
bp.foo = new(foo_) // allocate bp.foo
Which one to use depends on the properties of type bar
versus type barP
. Which type more closely reflects the problem that you are trying to solve?
For example, consider this invoice problem. We always have a billing address; we are always going to ask for our money. However, we often ship to the billing address, but not always. If the shipping address is nil
, use the billing address. Otherwise, use a separate shipping address. We have two warehouses, and we always ship from one or the other. We can share the two warehouse locations. Since we don't send an invoice until the order is shipped from the warehouse, the warehouse location will never be nil
.
type address struct {
street string
city string
}
type warehouse struct {
address string
}
type invoice struct {
name string
billing address
shipping *address
warehouse *warehouse
}
答案2
得分: 2
答案在很大程度上与语言无关 - 在C中的等效部分存在相同的问题。
当你有一个嵌入值(如bar
)时,你的结构足够大,可以容纳完整的子结构和其他部分。
当你有一个指向值的指针(如barP
)时,一些类型为barP
的结构可能共享相同的foo
。当任何一个barP
修改它指向的foo
的一部分时,它会影响到所有指向相同位置的其他barP
结构。此外,正如注释所建议的那样,你必须管理两个单独的对象 - barP
和foo
,而不是只有一个普通的bar
类型。
在某些语言中,你需要担心悬空指针和未初始化的值等问题;Go语言具有垃圾回收功能,并且通常比其他语言更具类型安全性。
因此,当你希望多个barP
对象共享相同的foo
对象时,请使用指针;否则,请使用显式成员对象,而不是对象的指针。
英文:
The answer is largely independent of language - the equivalent in C has the same issues.
When you have an embedded value (as in bar
), then your structure is big enough to hold the complete sub-structure and the other part.
When you have a pointer to a value (as in barP
), then a number of structures of type barP
may share the same foo
. When any of the barP
modifies a part of the foo
it points to, it affects all the other barP
structures that point to the same place. Also, as the commentary suggests, you have to manage two separate objects - the barP
and the foo
as against one with the plain bar
type.
In some languages, you would have to worry about dangling pointers and uninitialized values etc; Go is garbage collected and generally more type-safe than other languages.
So, use a pointer when you want multiple barP
objects to share the same foo
object; otherwise, use an explicit member object, rather than a pointer to an object.
答案3
得分: 2
Golang FAQ现在总结了以下两种方法之间的区别:
func (s *MyStruct) pointerMethod() { } // 指针方法
func (s MyStruct) valueMethod() { } // 值方法
> 首先,最重要的是,方法是否需要修改接收者?
如果需要修改,接收者必须是指针。(切片和映射是引用类型,所以它们的情况稍微复杂一些,但是例如在方法中改变切片的长度,接收者仍然必须是指针。)
在上面的例子中,如果pointerMethod
修改了s
的字段,调用者将看到这些更改,但是valueMethod
是使用调用者参数的副本调用的(这就是传值的定义),所以它所做的更改对调用者是不可见的。
顺便说一下,在Java中,指针接收者与此情况完全相同,尽管在Java中指针是隐藏在底层的;而Go的值接收者则是不寻常的。
> 其次是效率的考虑。如果接收者很大,比如一个大的结构体,使用指针接收者会更加高效。
(这个效率点也在“内存、内存中的变量和指针”中有所说明)
> 接下来是一致性。如果类型的某些方法必须具有指针接收者,那么其余的方法也应该具有指针接收者,这样无论类型如何使用,方法集都是一致的。有关详细信息,请参阅method sets部分。
英文:
The Golang FAQ now summarizes the difference between:
func (s *MyStruct) pointerMethod() { } // method on pointer
func (s MyStruct) valueMethod() { } // method on value
> First, and most important, does the method need to modify the receiver?
If it does, the receiver must be a pointer. (Slices and maps are reference types, so their story is a little more subtle, but for instance to change the length of a slice in a method the receiver must still be a pointer.)
In the examples above, if pointerMethod
modifies the fields of s
, the caller will see those changes, but valueMethod
is called with a copy of the caller's argument (that's the definition of passing a value), so changes it makes will be invisible to the caller.
By the way, pointer receivers are identical to the situation in Java, although in Java the pointers are hidden under the covers; it's Go's value receivers that are unusual.
> Second is the consideration of efficiency. If the receiver is large, a big struct for instance, it will be much cheaper to use a pointer receiver.
(This efficiency point is also illustrated in "Memory, variables in memory, and pointers ")
> Next is consistency. If some of the methods of the type must have pointer receivers, the rest should too, so the method set is consistent regardless of how the type is used. See the section on method sets for details.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论