Warning: file_get_contents(/data/phpspider/zhask/data//catemap/3/go/7.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Methods golang在T和*T上区分方法集的原因是什么?_Methods_Go - Fatal编程技术网

Methods golang在T和*T上区分方法集的原因是什么?

Methods golang在T和*T上区分方法集的原因是什么?,methods,go,Methods,Go,这是我在学习过程中最困惑的地方。我们都知道T上的方法只影响T的副本,而*T上的方法将影响T上的实际数据 为什么*T也可以使用*T上的方法,但不允许使用相反的方法?那么,你能给我一个例子(或理由)说明为什么他们不允许*T上的方法被T使用吗 这种设计的优缺点是什么?你的问题有答案的关键:T上的方法只影响T的副本。因此,有了这些信息,以下摘录将有助于澄清其余的困惑: 从Go规范: “任何其他命名类型T的方法集由具有 接收器类型T。对应指针类型*T的方法集 是具有receiver*T或T的所有方法的集合

这是我在学习过程中最困惑的地方。我们都知道
T
上的方法只影响
T
的副本,而
*T
上的方法将影响
T
上的实际数据

为什么
*T
也可以使用
*T
上的方法,但不允许使用相反的方法?那么,你能给我一个例子(或理由)说明为什么他们不允许
*T
上的方法被
T
使用吗


这种设计的优缺点是什么?

你的问题有答案的关键:
T
上的方法只影响
T
的副本。因此,有了这些信息,以下摘录将有助于澄清其余的困惑:

从Go规范:

“任何其他命名类型T的方法集由具有 接收器类型T。对应指针类型*T的方法集 是具有receiver*T或T的所有方法的集合(即,它也是 包含T的方法集。“

如果接口值包含 指针*T,方法调用可以通过解引用 指针,但如果接口值包含值T,则不存在 方法调用获取指针的有用方法

即使在编译器可以将值的地址 传递给该方法,如果该方法修改了值,则更改将 在呼叫者中迷失。作为一个常见示例,此代码:

var buf bytes.Buffer
io.Copy(buf, os.Stdin)
将复制标准输入 变成buf的副本,而不是buf本身。这几乎从来都不是问题 期望的行为


关于接口的最好文章之一是

它包括以下示例:

type Animal interface {
    Speak() string
}

type Dog struct {
}

func (d Dog) Speak() string {
    return "Woof!"
}

type Cat struct {
}

func (c *Cat) Speak() string {
    return "Meow!"
}
它解释道:

指针类型可以访问其关联值类型的方法,但反之亦然。
也就是说,
*Dog
值可以使用
Dog
上定义的
Speak
方法,但是正如我们前面看到的,
Cat
值不能访问
*Cat
上定义的
Speak
方法

(这反映了你的问题)

这听起来可能有点神秘,但当您记住以下内容时,它是有意义的:Go中的所有内容都是通过值传递的
每次调用函数时,传递到函数中的数据都会被复制。 对于带有值接收器的方法,调用该方法时会复制该值

当您了解以下签名的方法时,这一点就更加明显了:

是类型为
func(T,string)
的函数;方法接收器与任何其他参数一样,通过值传递到函数中

调用方将看不到在值类型(例如,
func(d Dog)Speak(){…}
)上定义的方法内部对接收方所做的任何更改,因为调用方正在确定一个完全独立的
Dog
值的范围

(即“按价值复制”部分)

由于所有内容都是通过值传递的,因此
*Cat
方法不能被
Cat
值使用的原因应该很明显;任何一个Cat值都可以有任意数量的指向它的
*Cat
指针。如果我们试图通过使用
Cat
值调用
*Cat
方法,那么我们从没有一个
*Cat
指针开始

相反,如果我们在
Dog
类型上有一个方法,并且我们有一个
*Dog
指针,我们知道调用此方法时要使用哪个
Dog
值,因为
*Dog
指针正好指向一个
Dog
值;Go运行时将在任何必要的时候取消对其关联
Dog
值的指针引用。
也就是说,给定
*Dog
d
Dog
类型上的方法
Speak
,我们可以说
d.Speak()
;我们不需要像在其他语言中那样说
d->Speak()


这里有很多答案,但没有一个能回答为什么会这样

首先,我们假设您有一个*T,并且想要调用一个取T的方法。要做到这一点,您只需要将*yourT(其中*用于取消引用指针)传递给函数。这保证是可能的,因为您只是在一个已知位置复制内存块

现在让我们假设你有一个T并且想要一个T。你可能在想,你可以这样做&你的T并得到它的地址。但生活并不总是那么简单。并不总是有一个静态地址可供选择


发件人:

对于类型为T的操作数x,地址操作&x生成一个类型为*T到x的指针。操作数必须是可寻址的,即变量、指针间接寻址或切片索引操作;或可寻址结构操作数的字段选择器;或可寻址数组的数组索引操作。作为可寻址性要求的一个例外,x也可以是一个(可能带括号的)复合文字

您可能会问自己,为什么他们会对获取内存地址设置这些任意限制。每个变量都必须有一些内存地址,对吗?虽然这是事实,但优化可以使这些地址变得相当短暂

例如,假设变量位于映射内:

res := TMap["key"].pointerMethod()
在这种情况下,您实际上是在说希望在映射中保存一个指向内存的指针。这将迫使Go-to-ImplementMap的每个实现保持内存地址的静态。这将严重限制运行时的内部结构,并使实现者在构建高效映射时的自由度大大降低

还有其他示例,如函数返回或接口,但您只需要一个示例来证明该操作不保证是可能的


归根结底,计算机内存并不是简单的w
res := TMap["key"].pointerMethod()