嵌入式指针的使用时机

huangapple go评论83阅读模式
英文:

Embedding when to use pointer

问题

当我想在一个结构体中嵌入另一个结构体时,我应该使用指针还是值?

例如:

type Job struct {
    Command string
    *log.Logger
}

还是

type Job struct {
    Command string
    log.Logger
}
英文:

When I want to embed a struct within another struct, should I use a pointer or value?

For example

type Job struct {
    Command string
    *log.Logger
}

or

type Job struct {
    Command string
    log.Logger
}

答案1

得分: 13

你可以使用其中之一:对于struct类型,规范中提到:

使用类型但没有显式字段名声明的字段是匿名字段,也称为嵌入字段或将类型嵌入结构中的字段。

嵌入类型必须指定为类型名T或非接口类型名*T的指针,并且T本身不能是指针类型。

由于log.Logger 不是接口,你可以对匿名字段Logger使用类型或类型的指针。


文章“在Go中嵌入”来自Eric Urban (hydrogen18),将指针嵌入称为“通过指针嵌入”:

  • 这样做的第一个优点是,你可以依赖使用NewX惯用法返回指向结构体的指针的函数进行初始化。
  • 第二个优点是,你可以嵌入类型的所有功能,而无需知道何时实例化它
    指向Bitmap的嵌入指针与Go中的任何其他指针没有区别,因此可以多次赋值。
    通过这样做,你可以在运行时动态更改要扩展的实例。

例如,使用以下代码:

type Bitmap struct{
    data [4][5]bool
}

type Renderer struct{
    *Bitmap //通过指针嵌入
    on uint8
    off uint8
}

Renderer类型通过指针嵌入了Bitmap

一个Bitmap的单个实例可以作为多个Renderer实例的嵌入实例:

var renderA,renderB Renderer
renderA.on = 'X'
renderA.off = 'O'
renderB.on = '@'
renderB.off = '.'

var pic Bitmap
pic.data[0][6] = true
pic.data[0][7] = true
pic.data[1][8] = true
pic.data[2][9] = true
pic.data[3][10] = true

renderA.Bitmap = &pic
renderB.Bitmap = &pic

renderA.render()
renderB.render()

这将共享相同的Bitmap实例给两个不同的渲染器。
每个渲染器都有自己的字符集,允许打印出位图的两种表示方式。
以下是输出的样子:

OXXO
OXOO
OXOO
OXOO
.@@.
.@..
.@..
.@..

这个示例演示了享元模式
虽然在这个示例中对内存消耗没有影响,但是许多共享单个底层数据结构的实例可以在减少系统内存消耗方面非常重要。


此线程中所述:

不能将指针嵌入和接口指针嵌入作为匿名字段的原因是这些类型没有方法。
匿名字段的整个目的是方法的提升

我已经解释过为什么接口没有方法:很多人错误地和不必要地使用指向接口的指针,而且没有已知的有效用途,因此通过使指向接口的指针没有方法来主动阻止这种用法,从而改变了语言。

英文:

You can use one or the other: for struct type, the spec mentions:

> A field declared with a type but no explicit field name is an anonymous field, also called an embedded field or an embedding of the type in the struct.

> An embedded type must be specified as a type name T or as a pointer to a non-interface type name *T, and T itself may not be a pointer type.

Since log.Logger is not an interface, you can use the type or a pointer to the type for the anonymous field Logger.


The article "Embedding in Go " fro Eric Urban (hydrogen18) calls embedding a pointer "embed by-pointer":

> - The first advantage to this is that you can rely on functions that use the NewX idiom returning a struct by-pointer to do initialization.

  • The second advantage is that you can embed all the functionality of a type without needing to know when it is instantiated.
    The embedded pointer to a Bitmap is no different than any other pointer in Go, so it can be assigned multiple times.
    By doing this you can change what instance you are extending dynamically at run time.

For instance, with:

type Bitmap struct{
    data [4][5]bool
}

type Renderer struct{
    *Bitmap //Embed by pointer
    on uint8
    off uint8
}

The Renderer type embeds a Bitmap by-pointer.

> A single instance of Bitmap can act as the embedded instance of many Renderer instances:

var renderA,renderB Renderer
renderA.on = 'X'
renderA.off = 'O'
renderB.on = '@'
renderB.off = '.'

var pic Bitmap
pic.data[0][6] = true
pic.data[0][7] = true
pic.data[1][8] = true
pic.data[2][9] = true
pic.data[3][10] = true

renderA.Bitmap = &pic
renderB.Bitmap = &pic

renderA.render()
renderB.render()

> This shares the same Bitmap instance to two different renderers.
Each renderer has its own set of characters, allowing two representations of the bitmap to be printed.
This is what the output looks like:

OXXO
OXOO
OXOO
OXOO
.@@.
.@..
.@..
.@..

> This example demonstrates the Flyweight Pattern.
Although inconsequential to memory consumption in this example, having many thousands of instances sharing a single underlying data structure can be very significant in reducing the memory consumption of systems.


As mentioned in this thread:

> The reason why you can't have pointer to pointer and pointer to interface anonymous fields is that these types don't have methods.
The whole point of anonymous fields is that methods get promoted.

> I already explained why interfaces don't have methods: a lot of people were using pointers to interfaces incorrectly and unnecessarily, and there weren't any known valid uses, so the language was changed to actively discourage this usage by making pointers to interfaces have no methods.

huangapple
  • 本文由 发表于 2015年1月2日 03:19:33
  • 转载请务必保留本文链接:https://go.coder-hub.com/27733854.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定