Struct 通过值或指针访问另一个结构

Struct 通过值或指针访问另一个结构,struct,pointers,go,Struct,Pointers,Go,通过值或指针访问另一个结构时有什么区别? 什么时候应该使用它们中的每一个 type foo_ struct { st uint8 nd uint8 } type bar struct { rd uint8 foo foo_ } type barP struct { rd uint8 foo *foo_ } 答案在很大程度上与语言无关——C语言中的等价物也有同样的问题 当您有一个嵌入值(如bar)时,您的结构就足够大,可以容纳完整的子结构和

通过值或指针访问另一个结构时有什么区别?
什么时候应该使用它们中的每一个

type foo_ struct {
    st uint8
    nd uint8
}

type bar struct {
    rd  uint8
    foo foo_
}

type barP struct {
    rd  uint8
    foo *foo_
}

答案在很大程度上与语言无关——C语言中的等价物也有同样的问题

当您有一个嵌入值(如
bar
)时,您的结构就足够大,可以容纳完整的子结构和其他部分

当您有一个指向某个值的指针(如
barP
)时,许多类型为
barP
的结构可能共享相同的
foo
。当任何
barP
修改它指向的
foo
的一部分时,它会影响指向同一位置的所有其他
barP
结构。此外,正如注释所示,您必须管理两个单独的对象,
barP
foo
,而不是普通的
bar
类型

在某些语言中,您必须担心指针悬空和未初始化的值等问题;Go是垃圾收集的,通常比其他语言更安全


因此,当您希望多个
barP
对象共享同一
foo
对象时,请使用指针;否则,请使用显式成员对象,而不是指向对象的指针。

如果声明或分配了
类型bar
的变量,则为
rd uint8
foo foo
保留并初始化为零内存。始终有一个
类型foo\ucode>的变量嵌入到
类型bar
的变量中

var b bar  // declare b

如果您声明或分配一个barP类型的
变量
,则为
rd uint8
foo*foo
保留并初始化为零内存。零值指针是
nil
指针。未分配类型为foo\u0的
变量;你必须分开做。有零(
foo==nil
)或一个
类型foo的变量被
类型barP
的变量指向。
类型barP
的变量可能指向与
类型barP
的其他变量相同的
类型foo
变量,共享
类型foo
变量的相同副本。指向共享副本的所有变量都可以看到对共享副本的更改

var bp barP         // declare bp
bp.foo = new(foo_)  // allocate 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
}
现在,本节总结了以下内容之间的区别:

func (s *MyStruct) pointerMethod() { } // method on pointer
func (s MyStruct)  valueMethod()   { } // method on value
首先,也是最重要的一点,该方法是否需要修改接收器?
如果是,则接收器必须是指针。(切片和贴图都是引用类型,因此它们的故事要微妙一些,但例如,要在方法中更改切片的长度,接收者仍然必须是指针。)
在上面的示例中,如果
pointerMethod
修改
s
的字段,调用方将看到这些更改,但是调用方的参数副本调用了
valueMethod
(这是传递值的定义),因此调用方将看不到它所做的更改。
顺便说一句,指针接收器与Java中的情况完全相同,尽管在Java中指针隐藏在盖子下面;Go的价值接受者与众不同

第二是对效率的考虑。如果接收器很大,例如一个大结构,那么使用指针接收器会便宜得多

(该效率点也在“”中说明)

接下来是一致性。如果该类型的某些方法必须具有指针接收器,那么其他方法也应该具有指针接收器,这样无论该类型如何使用,方法集都是一致的。有关详细信息,请参阅上的部分


当声明或分配barP
类型的变量时,foo的初始值将是
nil
指针。