当前位置: 首页 > 面试题库 >

当命名类型为T的任何方法具有指针接收器时,复制类型T的实例

庄博厚
2023-03-14
问题内容

我最近读了《
Go编程语言
,这是学习golang编程语言的好资源。在6.2节中有一段关于类型的副本实例T在方法中是否为指针接收者的段落,我无法理解。有没有什么可以用有意义的例子来解释本段的?

6.2使用指针接收器的方法

如果所有命名类型T的方法本身都具有接收者T类型(不是*
T),则可以安全地复制该类型的实例;调用其任何方法必然会产生一个副本。例如,time.Duration值被自由地复制,包括作为函数的参数。但是,如果任何方法都有指针接收器,则应避免复制T的实例,因为这样做可能会违反内部不变式。例如,复制bytes.Buffer的实例将导致原始副本和别名(第2.3.2节)相同的底层字节数组。随后的方法调用将产生不可预测的影响。

(Go编程语言Alan AA Donovan·Brian W. Kernighan)


问题答案:

调用方法时,将首先复制调用该方法的值,然后将该副本传递/用作接收方。

如果类型仅具有带有值接收器的方法,则意味着无论方法内部执行什么操作,也无论您(或其他任何人)调用什么方法,这些方法都将无法更改 原始
值,因为–如前所述上面-仅传递副本,并且该方法只能修改副本-而不是原始副本。

因此,这意味着如果您复制该值,则不必担心,原始文件或副本上调用的方法都不会/不会修改该值。

否,当类型具有带有指针接收器的方法时。如果方法具有指针接收器,则该方法可以更改/修改 指向的 值,该值不是副本,而是原始值(仅 指针
是副本,但指向原始值)。

让我们来看一个例子。我们创建一个int包装器类型,其中包含2个字段:an int和an
*int。我们打算在两个字段中存储相同的数字,但是一个是指针(并且我们将其存储int指向的 值中):

type Wrapper struct {
    v int
    p *int
}

为确保两个值(v*p)相同,我们提供了Set()一种设置两个值的方法:

func (w *Wrapper) Set(v int) {
    w.v = v
    *w.p = v
}

Wrapper.Set()具有指针接收器(*Wrapper),因为它必须修改值(类型为Wrapper)。无论我们传递给哪个数字Set(),我们都可以确保一旦Set()返回,两者v*p将相同,并且等于传递给的数字Set()

现在,如果我们的值为Wrapper

a := Wrapper{v: 0, p: new(int)}

我们可以Set()在上面调用该方法:

a.Set(1)

编译器将自动采用的地址a作为接收器,因此上述代码表示(&a).Set(1)

如果只使用方法来更改字段的值,我们希望任何类型的值WrapperWrapper.v和中都具有相同的数字。*Wrapper.pv``Set()

现在,让我们看一下如果我们复制的问题a

a := Wrapper{v: 0, p: new(int)}
b := a
fmt.Printf("a.v=%d, a.p=%d;  b.v=%d, b.p=%d\n", a.v, *a.p, b.v, *b.p)

a.Set(1)
fmt.Printf("a.v=%d, a.p=%d;  b.v=%d, b.p=%d\n", a.v, *a.p, b.v, *b.p)

输出(在Go Playground上尝试):

a.v=0, a.p=0;  b.v=0, b.p=0
a.v=1, a.p=1;  b.v=0, b.p=1

我们制作了一个副本a(将其存储在中b),并打印了值。到目前为止,一切都很好。然后我们调用a.Set(1),之后它a仍然很好,但是的内部状态b变为无效:b.v不再等于*b.p。解释很明确:当我们复制a(是一种struct类型)时,将复制其字段的值(包括指针p),并且in中的指针b将指向与in中的指针相同的值a。因此,修改指标值将影响的两个副本Wrapper,但我们有2个不同的v字段(它们是非指标)。

如果您有使用指针接收器的方法,则应使用指针值。

请注意,如果您复制一个的值*Wrapper,那么一切仍然会很酷:

a := &Wrapper{v: 0, p: new(int)}
b := a
fmt.Printf("a.v=%d, a.p=%d;  b.v=%d, b.p=%d\n", a.v, *a.p, b.v, *b.p)

a.Set(1)
fmt.Printf("a.v=%d, a.p=%d;  b.v=%d, b.p=%d\n", a.v, *a.p, b.v, *b.p)

输出(在Go Playground上尝试):

a.v=0, a.p=0;  b.v=0, b.p=0
a.v=1, a.p=1;  b.v=1, b.p=1

在这种情况下a是一个指针,它的类型是*Wrapper。我们做它的一个副本(其存储在b),被称为a.Set(),以及两者的内部状态ab仍然有效。这里我们只有一个Wrapper值,a只保存一个指向它的指针(它的地址)。复制时a,我们只会复制指针值,而不复制struct值(类型Wrapper)。



 类似资料:
  • 问题内容: Go spec说: 其他任何类型T的方法集都包含接收者类型T的所有方法。相应指针类型 T的方法集是接收者 T或T的所有方法的集合(也就是说,它还包含T)。 我的理解是:T有自己的方法集,而 T有自己的方法集加上T的方法集,因为它可以将接收者 T取消引用到T并调用该方法。因此,我们可以使用变量T的接收者* T调用某些方法。 因此,我决定验证自己的逻辑: http://play.golan

  • 我的问题标题有点模糊,但本质上我想实现以下几点: struct Foo实现行为A和行为B和行为C 结构栏实现行为A Foo和Bar都实现了一些Content特性 从一个

  • 问题内容: 我有一个泛型类。在一种方法中,我想获取类型T的类实例,但是我不能调用。 使用它解决问题的首选方法是什么? 问题答案: 简短的答案是,无法找到Java中泛型类型参数的运行时类型。我建议阅读Java教程中有关类型擦除的章节以获取更多详细信息。 一个流行的解决方案是将type参数的传递给泛型类型的构造函数,例如

  • 问题内容: 我上课了。我怎么能说我想成为某个班级的实施者?编写根本无法编译。 问题答案: 使用代替。

  • 问题内容: 根据这个问题的回答 关于指针与接收器的值的规则是,可以在指针和值上调用值方法,但是只能在指针上调用指针方法 但实际上我可以对非指针值执行指针方法: 那么,这是怎么了?这是新功能吗?还是对问题的回答是错误的? 问题答案: 您正在对指针值调用“指针方法”。在表达式中: 是类型(非指针);由于该方法具有指针接收者,并且由于接收者值是非指针且可寻址,因此它是以下各项的简写形式: 这是在规范中:

  • 问题内容: 我有一个泛型类。在一种方法中,我想获取的类实例,但我无法调用。 使用它解决问题的首选方法是什么? 问题答案: 简短的答案是,无法找到Java中泛型类型参数的运行时类型。我建议阅读Java教程中有关类型擦除的章节以获取更多详细信息。 一个流行的解决方案是Class将type参数的传递给泛型类型的构造函数,例如