【问题标题】:My object is not updated even if I use the pointer to a type to update it即使我使用指向类型的指针来更新它,我的对象也没有更新
【发布时间】:2017-10-08 23:56:48
【问题描述】:

我将一些 Individual 对象存储在一个切片中。在将其附加到切片之前,我打印了 Individual 对象的名称。

将其存储在切片中后,我将其作为指针检索并希望将名称更改为"Peter",但更改不起作用,因为它仍然打印"Steve"。为什么?

type Individual interface {
    GetName() *string
    SetName(name string)
}

type Person struct {
    name string
}

// Implement functions of the Individual interface
func (p Person) GetName() *string  {
    return &p.name
}

func (p Person) SetName(newName string)  {
    name := p.GetName();
    *name = newName
}


var individuals []Individual

func main() {
    person := Person{name: "Steve"}
    fmt.Println(person)

    individuals = append(individuals, person) // append Person to slice
    p1 := individuals[0]     // Retrieve the only Person from slice
    p1.SetName("Peter")      // Change name
    fmt.Println(p1)          // Should print "Peter", but prints "Steve"
    fmt.Println(person)      // Should print "Peter", but prints "Steve"
}

【问题讨论】:

    标签: pointers go methods slice


    【解决方案1】:

    解决这个问题的两件事:

    • 您需要通过“指针”方法附加方法,否则名称只会在方法内部更改。
    • 您需要为实际的Person 变量使用指针,因为它们需要实现一个接口
    type Individual interface {
        GetName() *string
        SetName(name string)
    }
    
    type Person struct {
        name string
    }
    
    // Implement functions of the Individual interface
    func (p Person) GetName() *string  {
        return &p.name
    }
    
    func (p *Person) SetName(newName string)  {
        p.name = newName
    }
    
    
    var individuals []Individual
    
    func main() {
        person := &Person{name: "Steve"}
        fmt.Println(person)
    
        individuals = append(individuals, person) // append Person to slice
        p1 := individuals[0]     // Retrieve the only Person from slice
        p1.SetName("Peter")      // Change name
        fmt.Println(p1)          // Prints "Steve"
        fmt.Println(person)      // Prints "Steve"
    }
    

    Go Playground 上的示例。

    【讨论】:

    • 就目前而言,我认为GetName 有点误导。它在Person 的副本中返回指向string 的指针。我假设该方法应该有一个指针接收器或按值返回,不是吗?
    【解决方案2】:

    每当一个方法想要修改接收者时,必须是指向值的指针;该方法必须有一个指针接收器。

    如果一个方法有一个非指针接收器,则在调用该方法时会生成并传递一个副本。从那时起,无论你对接收者做什么,你都只能修改副本,而不能修改原件。

    所以在你的例子中:

    func (p Person) SetName(newName string)  {
        name := p.GetName();
        *name = newName
    }
    

    当调用SetName() 时,会复制Person。在SetName() 中,您将获得您修改的copyname 字段的地址。 (其实是副本的副本,稍后会详细介绍...)

    解决方案:使用指针接收器:

    func (p *Person) SetName(newName string)  {
        name := p.GetName();
        *name = newName
    }
    

    从现在开始,只有*Person实现了Individual,所以在追加时使用指针:

    individuals = append(individuals, &person)
    

    这很棘手,因为在此之后它就无法工作了。这是为什么呢?

    这是因为Person.GetName() 仍然有一个非指针接收器:

    func (p Person) GetName() *string {
        return &p.name
    }
    

    所以当从SetName()调用GetName()时,会再次进行复制,GetName()会返回copyname字段的地址,@987654338 @ 只会修改为调用 GetName() 而创建的副本。

    因此,为了使所有工作正常进行,您还必须为GetName() 使用指针接收器:

    func (p *Person) GetName() *string {
        return &p.name
    }
    

    现在它正在工作,输出(在Go Playground 上试试):

    {Steve}
    &{Peter}
    {Peter}
    

    但要知道,最简单和推荐的方法很简单:

    func (p *Person) SetName(newName string) {
        p.name = newName
    }
    

    仅此而已。

    【讨论】:

    • 我认为GetName 按值返回string 会更惯用?
    • 感谢您的好评!如果我多次调用p1 := individuals[0],然后在每次调用后用fmt.Println(&p1) 打印地址,我每次都会得到不同的地址。 p2 := individuals[0] 然后fmt.Println(&p2) 将打印另一个地址。为什么?
    • @ChrisDrew 是的,很明显。我只是想从代码中引导提问者提供的内容,因为我认为它具有启发性/启发性(copy of copy 部分并非不言而喻)。
    • @Rox p1 := individuals[0] 是一个 short variable declaration,它创建了一个局部变量,&p1局部变量 的地址(而不是指针值应该它包含一个指针),每次都会(可能)不同。
    【解决方案3】:

    https://play.golang.org/p/eg8oYDV6Xx p1 和 p2 已经是地址,你不需要它们的地址(地址的地址),只需打印 p1 和 p2 - 它们会和你看到的一样。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2023-02-09
      • 2014-04-24
      • 2020-08-30
      • 1970-01-01
      • 1970-01-01
      • 2021-07-02
      • 2023-01-12
      相关资源
      最近更新 更多