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()