【问题标题】:Embedding struct vs pointer to struct in struct used as pointer在用作指针的结构中嵌入结构与指向结构的指针
【发布时间】:2019-07-27 23:35:01
【问题描述】:

如果我有一个结构类型A 用作指针(只有指针接收器,构造函数返回*A 等),那么将结构类型B 嵌入为@987654324 有什么区别@ 与 *B?

也就是有什么区别

type B struct {...}
type A struct {
    B
    // ...
}

type B struct {...}
type A struct {
    *B
    // ...
}

例如,是否存在对嵌入字段的复制?

编辑:我还应该提到嵌入式结构B 只有指针接收器。

【问题讨论】:

    标签: go struct


    【解决方案1】:

    两种结构的零值不同,这可能是人体工程学的显着差异。

    考虑一个嵌入类型

    type B struct {
        X int
    }
    
    func (b *B) Print() { fmt.Printf("%d\n", b.X) }
    

    如果我们直接将其嵌入为对象

    type AObj struct {
        B
    }
    

    那么AObj类型的零值包含了一个B类型的嵌入对象,它也有它的零值,因此我们可以安全地

    var aObj AObj
    aObj.Print() // prints 0
    

    但是如果我们改为嵌入一个指针

    type APtr struct {
        *B
    }
    

    这个struct的零值有一个nil指针值,我们不能真正直接使用。

    var aPtr APtr
    aPtr.Print() // panics
    

    希望以您期望的方式复制对象。如果您创建一个新的AObj 对象,它会获得嵌入的B 的副本。

    aObj2 := aObj
    aObj.X = 1
    aObj2.Print() // prints 0, because it has a copy
    

    如果您创建一个新的APtr 对象,它会获得*B 的副本,这意味着它共享底层的具体对象。

    aPtr.B = &B{}
    aPtr2 := aPtr
    aPtr.X = 1
    aPtr2.Print() // prints 1, because both objects point at the same B
    

    https://play.golang.org/p/XmOgegwVFeE 的可运行示例

    【讨论】:

      【解决方案2】:

      考虑一个简单的示例程序。 structAPtr 嵌入指针,structAVal 直接嵌入struct structB

      package main
      
      import "fmt"
      
      type structB struct {
          foo int
      }
      
      type structAPtr struct {
          bar *structB
      }
      
      type structAVal struct {
          bar structB
      }
      
      func main() {
          // referencing bStruct
          b1 := structB{foo: 12}
      
          aPtr := structAPtr{bar: &b1}
          fmt.Println("Before assignment:")
          fmt.Printf("aPtr.bar.foo = %d, b.foo = %d\n", aPtr.bar.foo, b1.foo)
      
          aPtr.bar.foo = 42
          fmt.Println("After assignment:")
          fmt.Printf("aPtr.bar.foo = %d, b.foo = %d\n", aPtr.bar.foo, b1.foo)
      
          // copying bStruct
          b2 := structB{foo: 12}
      
          aVal := structAVal{bar: b2}
          fmt.Println("Before assignment:")
          fmt.Printf("aVal.bar.foo = %d, b.foo = %d\n", aVal.bar.foo, b2.foo)
      
          aVal.bar.foo = 42
          fmt.Println("After assignment:")
          fmt.Printf("aVal.bar.foo = %d, b.foo = %d\n", aVal.bar.foo, b2.foo)
      }
      

      int structB.foo 用于演示在structAPtrstructAVal 内部操作时structB 是否发生变化。

      这个程序输出:

      Before assignment:
      aPtr.bar.foo = 12, b.foo = 12
      After assignment:
      aPtr.bar.foo = 42, b.foo = 42 <------------ both changed
      Before assignment:
      aVal.bar.foo = 12, b.foo = 12
      After assignment:
      aVal.bar.foo = 42, b.foo = 12 <------------ only assignee changed
      

      查看结果显示:

      • 指针的值更改为structB会更改structB

      • structAVal 中更改复制版本structB 的值使structB 不受影响(即使在将42 分配给aVal 之后,它仍然是5


      编辑:

      如果您的structB 无论如何都只有指针接收器,那么预期的行为可能是更改strucA 中的structB 会同时更新它们。这是我的示例中的场景 1,并且肯定需要一个指针。来自围棋之旅

      带有指针接收器的方法可以修改接收器指向的值 [...]。由于方法经常需要修改它们的接收器,因此指针接收器比值接收器更常见。


      希望有帮助!

      【讨论】:

        猜你喜欢
        • 2010-11-14
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多