即使我使用指向类型的指针来更新它,我的对象也没有更新

Posted

技术标签:

【中文标题】即使我使用指向类型的指针来更新它,我的对象也没有更新【英文标题】: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 := Personname: "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"

【问题讨论】:

【参考方案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 := &Personname: "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 - 它们会和你看到的一样。

【讨论】:

以上是关于即使我使用指向类型的指针来更新它,我的对象也没有更新的主要内容,如果未能解决你的问题,请参考以下文章

为啥即使我们将指针分配给NULL,指针的指向对象的大小也不为零?

在指向对象的不同类型的指针之间切换

指向具有私有构造函数的类的类成员的指针

[C++11新特性] 智能指针详解

指向对象开头的指针 (C++)

删除时出错:表达式必须是指向完整对象类型的指针